3 Stimmen

Synchronizing eingebettetes Python in einem mehrfädigen Programm

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?

4voto

Gareth Rees Punkte 62623

Wenn CPython eine Funktion aufruft, die blockieren könnte (oder wieder Python betritt), gibt es das globale Interpreter-Sperre frei, bevor es die Funktion aufruft, und erlangt dann die Sperre wieder zurück, nachdem die Funktion zurückkehrt. In Ihrem Code ist es Ihr Aufruf an die integrierte print-Funktion, der dazu führt, dass die Interpreter-Sperre freigegeben wird und der andere Thread ausgeführt wird (siehe string_print in stringobject.c).

Also benötigen Sie Ihre eigene Sperre: Die globale Interpreter-Sperre ist nicht geeignet, um die Serialisierung von Python-Code sicherzustellen, der E / A durchführt.

Da Sie das Boost-Thread-Framework verwenden, finden Sie es wahrscheinlich am praktischsten, eine der Boost Thread-Synchronisierungsprimitive zu verwenden, z. B. boost::interprocess::interprocess_mutex.

[Bearbeitet: Meine ursprüngliche Antwort war falsch, wie von Abyx festgestellt.]

2voto

Abyx Punkte 11461

with-Anweisung hat ein Problem in Python 3.1, aber es wurde in Python 3.2 und Python 2.7 behoben.

Die richtige Lösung ist daher die Verwendung des Moduls threading zur Synchronisierung.

Um solche Probleme zu vermeiden, sollte man keinen Mehrfaden-Code verwenden, der temporäre Variablen im globalen Wörterbuch verwendet, oder unterschiedliche globale Wörterbücher für jeden Thread verwenden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X