Hier ist ein Beispiel für die Verwendung des Python-Interpreters in einem mehrfädigen Programm:
#include
#include
void f(const char* code)
{
static volatile auto counter = 0;
for(; counter < 20; ++counter)
{
auto state = PyGILState_Ensure();
PyRun_SimpleString(code);
PyGILState_Release(state);
boost::this_thread::yield();
}
}
int main()
{
PyEval_InitThreads();
Py_Initialize();
PyRun_SimpleString("x = 0\n");
auto mainstate = PyEval_SaveThread();
auto thread1 = boost::thread(f, "print('thread #1, x =', x)\nx += 1\n");
auto thread2 = boost::thread(f, "print('thread #2, x =', x)\nx += 1\n");
thread1.join();
thread2.join();
PyEval_RestoreThread(mainstate);
Py_Finalize();
}
Es sieht gut aus, aber es ist nicht synchronisiert. Der Python-Interpreter gibt mehrmals den GIL frei und erwirbt ihn wieder während PyRun_SimpleString (siehe Dokumente, S.#2).
Wir können den Aufruf von PyRun_SimpleString seriellisieren, indem wir unser eigenes Synchronisierungsobjekt verwenden, aber das ist der falsche Weg.
Python hat seine eigenen Synchronisierungsmodule - _thread
und threading
. Aber sie funktionieren nicht in diesem Code:
Py_Initialize();
PyRun_SimpleString(R"(
import _thread
sync = _thread.allocate_lock()
x = 0
)");
auto mainstate = PyEval_SaveThread();
auto thread1 = boost::thread(f, R"(
with sync:
print('thread #1, x =', x)
x += 1
)");
- es wird ein Fehler ausgegeben
Datei "", Zeile 3, in NameError: name '_[1]' is not defined
und führt zu Deadlocks.
Wie synchronisiert man eingebetteten Python-Code auf effizienteste Weise?