2012-05-16 44 views
16

私はマルチスレッドのCアプリケーションにPythonインタプリタを埋め込みました。スレッドの安全性を確保するために使用するAPIについて少し混乱しています。マルチスレッドのCアプリケーションにPythonを埋め込む

私が集めたところから、Pythonを埋め込むときに、他のPython C API呼び出しを呼び出す前にGILロックを処理するのは組み込み関数です。これは次の関数で行われます。

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

しかし、これだけでは十分ではないようです。私はPython APIの相互排除を提供していないようだから、まだランダムなクラッシュを受けています。

いくつかのより多くのドキュメントを読んだ後、私も追加:右Py_IsInitialized()を呼び出した後

PyEval_InitThreads(); 

をしかし紛らわしい部分が出番です。 。

初期化とグローバルインタプリタロックを獲得

これは時にこの関数が戻り、GILがロックされるようにして何とかロックを解除しなければならないはずであることを示唆している:ドキュメントはこの機能はと述べています実際にはこれは必須ではないようです。この行を使用すると、マルチスレッドは完全に機能し、相互排除はPyGILState_Ensure/Release関数によって維持されました。
PyEval_ReleaseLock()PyEval_ReleaseLock()の後に追加しようとすると、後でPyImport_ExecCodeModule()という呼び出しでアプリがデッドロックしてしまいます。

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

答えて

4

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

PyEval_InitThreads(); 

後あなたは適切にメインスレッドのためにGILを解放しながら

PyEval_SaveThread(); 

を呼び出す必要があります。

+0

これは間違っており、潜在的に有害である: 'PyEval_SaveThread'は常に' PyEval_RestoreThread'と一緒になければなりません。 [他の場所で説明されているように](http://stackoverflow.com/a/15471525/1600898)、初期化後にロックを解除しないでください。 Pythonに任せて、通常の作業の一部としてリリースしてください。 – user4815162342

+0

Pythonへのすべての呼び出しを_Block_ _Allow_ブロックに入れると、なぜそれが有害なのかわかりません。一方、PyEval_SaveThread();を呼び出さないと、メインスレッドはPythonへの他のスレッドのアクセスをブロックします。つまり、 'PyGILState_Ensure()'デッドロックです。 – khkarens

+0

これは、Pythonの組み込みと拡張モジュールへの呼び出しの両方で機能する唯一のものです。 –

-1

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

CスレッドがPythonと通信するのであれば、Pythonアプリケーションがマルチスレッドであってもロックする心配はありません。 複数のPythonスレッドが必要な場合は、アプリケーションをこのように設定し、複数のCスレッドが複数のPythonスレッドにそれらを提供する単一のCスレッドとキューを介して通信するようにすることができます。

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

また、スタックレスPythonインタプリタがあります。それはGILとは離れていますが、複数のスレッドにバインドする他の問題に遭遇しているかどうかはわかりません。 stacklessは私の(シングルスレッドの)Cアプリケーションの代わりのものでした。

+1

あなたは実際に質問に答えないように最善を尽くしました。私は単一のスレッドに作業をキューイングするのに興味がありません。 – shoosh

5

私はまったく同じ問題を抱えていましたが、PyEval_InitThreads()の直後にPyEval_SaveThread()を使用して解決しました。しかし、私の実際の問題は、PyInitialise()の後にPyEval_InitThreads()を使用したことで、後続の異なるネイティブスレッドから呼び出されたときにブロックされることになりました(PyGILState_Ensure())。要約すると、これは私が今何をすべきかです:

  1. グローバル変数があります:

    static int gil_init = 0; 
    
  2. メインスレッドからネイティブC拡張をロードし、Pythonインタプリタを起動します。

    Py_Initialize() 
    
  3. 他の複数のスレッドから、私のアプリケーションは同時にPython/C APIに多くの呼び出しを行います:

    if (!gil_init) { 
        gil_init = 1; 
        PyEval_InitThreads(); 
        PyEval_SaveThread(); 
    } 
    state = PyGILState_Ensure(); 
    // Call Python/C API functions...  
    PyGILState_Release(state); 
    
  4. メインスレッドから私はPyGILState_Ensure()を使用してブロック/ランダムPythonのsigfaultsやデッドロックの原因となったのいずれか試した他のすべてのソリューション

    Py_Finalize() 
    

Pythonインタプリタを停止します。

Pythonのドキュメンテーションは、実際にはこれをより明確にすべきであり、少なくとも、埋め込みと拡張の両方の使用例についての例を提供してください。

関連する問題