Embeded Multi-Threaded Python: PyEval_InitThread(), PyEval_SaveThread(),...

Titus Brown t at chabry.caltech.edu
Wed Sep 19 15:24:43 EDT 2001


In article <8e0ac4fb.0109191028.5c2c1cda at posting.google.com>,
Nicolas Duchastel <nicolas at otelnet.com> wrote:
>I am trying to use the Python/C API function to get an embeded Interpreter
>into a multi-threaded C++ application. We are using Pthreads on Solaris 7.
>My application works fine, but only with 1 thread; and then, when I stop it,
>it core dumps.
>I am using Python 2.1.1 built on Solaris 7 with the SUN's C++ 5.2 compiler.

Hi, Nicolas,

you might be interested in the PyWX source code: pywx.idyll.org/.  It's
an LGPL'd project that put an embedded Python interpreter into AOLserver,
and it (correctly ;) deals with all of the issues you are facing.

Keeping the intricacies of the Python threading model in my head doesn't
seem to have worked too well, but here goes:

>I have an C++ application which creates a bunch of threads and then
>accepts messages. Each messages is a request to execute some Python code.
>Here's some pseudo-code of a 4 threaded system:
>   thread A: // INIT thread
>       do init stuff;
>       init Python interpreter:
>           Py_Initializ();
>           PyImport_ImportModule("MyModule");
>           ...
>       start thread B;
>       start thread C;
>       end this thread;

I would do things in this order:

--
Py_Initialize();
PyEval_InitThreads();			// grabs global lock

/* start pthreads stuff -- actual OS threading -- here */
--

Before you do things with threads B and C, you'll want to have a separate
Interpreter or ThreadState.

>   thread B and C: // WORKER thread
>       wait and receive a new message;
>       if (msg is exit_message)
>          start Thread D;
>          stop other worker thread (B or C);
>          end this thread;
>       else
>          call python code:
>             PyRun_SimpleString(...);
>            OR
>             PyObbject_CallMethod(...);

--
// grab Python lock
// if exit message, tell all other threads to exit and alert D;
// else call Python code
// release Python lock
--
>   thread D: // CLEAN-UP thread
>      wait for threads B and C to be stopped;
>      clean-up stuff
>      Py_Finalize();

what you have above, roughly.

>QUESTIONS:
>---------
> 1) what are the calls that I should be making !? in what order ?
>    any examples ? (i.e. not example of Python code executing some
>    multi-threaded stuff using the thread module, but rather some C/C++
>    code using the API C functions to implement a multi-threaded Interpreter).

See PyWX for actual examples...

> 2) what is the init sequence ? i.e. should PyEval_InitThreads be called ?
>    if so, before or after Py_Initialized() ? What objects need to be created ?
>    How ? (e.g. how do you create the locks ? what about the Thread State ?)

Py_Initialize should be followed by InitThreads.

> 3) does the Python interpreter remember which OS thread is running what ?

No.

>    i.e. one of my hypothesis for explaining the core dump is that the
>         interpreter is saying something like "Hey! You called Py_Initialize()
>         on thread t at 1, you called PyObject_CallMethod() on thread t at 4 and
>         now you are calling Py_Finalize() on thread t at 7; what the #@$ are
>         you trying to do!?"

That sounds pretty reasonable to me ;).

>    ---> do I need to create a ThreadState object for every OS thread which
>         could possibly use the Python Interpreter ? or is it, on per
>         concurent thread ?

I believe, technically, that you'll want to create a ThreadState object
for every Python execution process whose execution you want interleaved.
ThreadStates execute Python code in sequence...

> 4) when executing my message (i.e. logic in threads B or C), what do I
>    have to do to lock ? When do I need to lock ? or unlock ?
>    Which methods: PyEval_SavedThread(), PyEval_AcquireThread(),
>                   PyEval_AcquireLock(), Py_BEGIN_ALLOW_THREADS,.... ?

You'll want to allow threads *after* your C++ code is finished doing
C API calls into Python, and grab the lock *before* your C++ code starts
doing C API calls into Python.  Python code itself is sprinkled with the
statements so you don't need to worry about executing bodies of Python code.

Note also that PyEval_InitThreads grabs the lock, so you'll need to do a
lock release before the end of your A function.

It took me a few weeks to get PyWX working well with all of this; I'd be happy
to correspond with you after you take a look at that, to help explain stuff.
I find that the C API in Python for this stuff is confusing unless you have
a handle on what functions exist ;), so that's why I'd suggest starting to play.

Once it all works, it's great, though ;).

cheers,
--titus



More information about the Python-list mailing list