19

私はマルチスレッドCアプリケーションにPythonインタープリターを埋め込んでいますが、スレッドセーフを確保するためにどのAPIを使用すべきかについて少し混乱しています。

私が収集したものから、Pythonを埋め込む場合、他のPython C API呼び出しを呼び出す前に、GILロックを処理するのは埋め込み者次第です。これは、次の関数で実行されます。

gstate = PyGILState_Ensure();
// do some python api calls, run python scripts
PyGILState_Release(gstate);

しかし、これだけでは十分ではないようです。Python APIの相互排除を提供していないように見えるため、ランダムにクラッシュすることがあります。

さらにいくつかのドキュメントを読んだ後、私も追加しました:

PyEval_InitThreads();

呼び出しの直後Py_IsInitialized()ですが、それが混乱を招く部分です。ドキュメントには、この関数は次のように記載されています。

グローバルインタプリタロックを初期化して取得する

これは、この関数が戻ったときに、GILがロックされているはずであり、何らかの方法でロックを解除する必要があることを示しています。しかし実際には、これは必須ではないようです。この行を配置すると、マルチスレッドが完全に機能し、PyGILState_Ensure/Release関数によって相互排除が維持されました。に続く呼び出しでアプリがデッドロックした後、すぐに
追加しようとしたとき。PyEval_ReleaseLock()PyEval_ReleaseLock()PyImport_ExecCodeModule()

だから私はここで何が欠けていますか?

4

4 に答える 4

4

最終的に私はそれを理解しました。

PyEval_InitThreads();

あなたは電話する必要があります

PyEval_SaveThread();

メインスレッドのGILを適切に解放します。

于 2012-05-22T12:33:10.907 に答える
0

if (!gil_init) {@ formanの回答のコードは1回しか実行されないため、メインスレッドでも同じように実行できることに注意してください。これにより、フラグを削除できます(適切gil_initにアトミックまたは同期する必要があります)。

PyEval_InitThreads()は CPython 3.6 以前でのみ意味があり、CPython 3.9 では非推奨になっているため、マクロで保護する必要があります。

これらすべてを考えると、私が現在使用しているのは次のとおりです。

メインスレッドで、すべてを実行します

Py_Initialize();
PyEval_InitThreads(); // only on Python 3.6 or older!
/* tstate = */ PyEval_SaveThread(); // maybe save the return value if you need it later

これで、Python を呼び出す必要があるときはいつでも、

state = PyGILState_Ensure();
// Call Python/C API functions...    
PyGILState_Release(state);

最後に、メイン スレッドから、Python インタープリターを停止します。

PyGILState_Ensure(); // PyEval_RestoreThread(tstate); seems to work just as well
Py_Finalize()
于 2022-01-22T18:16:56.673 に答える
-1

複数のスレッドから単一の CPython インスタンスの複数の Python スレッドに通信しようとするマルチスレッド C アプリを持つことは、私には危険に見えます。

Python と通信する C スレッドが 1 つだけであれば、Python アプリケーションがマルチスレッドであっても、ロックについて心配する必要はありません。複数の Python スレッドが必要な場合は、この方法でアプリケーションをセットアップし、複数の C スレッドが、それらを複数の Python スレッドにファームアウトする単一の C スレッドとキューを介して通信するようにすることができます。

別の方法として、必要な C スレッドごとに複数の CPython インスタンスを用意することもできます (もちろん、Python プログラム間の通信は C プログラムを介して行う必要があります)。

別の代替手段として、Stackless Python インタープリターが考えられます。これで GIL はなくなりますが、GIL を複数のスレッドにバインドする他の問題が発生するかどうかはわかりません。stackless は、私の (シングルスレッドの) C アプリケーションのドロップイン代替品でした。

于 2012-05-17T05:50:12.253 に答える