2

boost :: pythonがpythonとのすべての相互作用のためにPythonGILを制御するようにする方法はありますか?

私はboost::pythonでプロジェクトを書いています。外部ライブラリのC++ラッパーを作成し、PythonスクリプトでC++ライブラリを制御しようとしています。外部ライブラリを変更できません。ラッパープログラムのみを変更できます。(私は上記の外部ライブラリの機能テストアプリケーションを書いています)

外部ライブラリはCで記述されており、関数ポインタとコールバックを使用して多くの手間のかかる作業を行います。そのメッセージングシステムであるため、メッセージが着信すると、たとえばコールバック関数が呼び出されます。

複数のオブジェクトが1つのコールバックをリッスンできるように、ライブラリにオブザーバーパターンを実装しました。私はすべての主要なプレーヤーを適切にエクスポートしており、特定の時点まで非常にうまく制御できます。

外部ライブラリは、メッセージの処理、メッセージの送信、処理などを行うスレッドを作成します。これらのコールバックの一部は異なるプロセスから呼び出される可能性があり、最近、Pythonはスレッドセーフではないことがわかりました。

これらのオブザーバーはPythonで定義できるため、Pythonを呼び出すことができる必要があり、Pythonはいつでもプログラムを呼び出す必要があります。

オブジェクトとオブザーバーをそのように設定します

class TestObserver( MyLib.ConnectionObserver ):
    def receivedMsg( self, msg ):
        print("Received a message!")

ob = TestObserver()
cnx = MyLib.Conection()
cnx.attachObserver( ob )

次に、接続に送信するソースを作成し、receivedMsg関数を呼び出します。

したがって、通常のsource.send('msg')はC ++アプリに移動し、Cライブラリに移動してメッセージを送信します。接続はメッセージを取得し、コールバックを呼び出します。コールバックはC++ライブラリに戻ります。 connectionは、すべてのオブザーバーに通知しようとします。この時点では、ここではpythonクラスであるため、そのメソッドを呼び出します。

そしてもちろん、コールバックはメインのアプリケーションスレッドではなく、接続スレッドから呼び出されます。

昨日はすべてがクラッシュしていて、1つのメッセージを送信できませんでした。次に、Cplusplus-sigアーカイブを調べた後、GILと、物事をロックするためのいくつかの気の利いた機能について学びました。

したがって、オブザーバークラスのC++pythonラッパーは次のようになります

struct IConnectionObserver_wrapper : Observers::IConnectionObserver, wrapper<Observers::IConnectionObserver>
{
    void receivedMsg( const Message* msg )
    {
        PyGILState_STATE gstate = PyGILState_Ensure();
        if( override receivedMsg_func = this->get_override( "receivedMsg" ) )
            receivedMsg_func( msg );
        Observers::IConnectionObserver::receivedMsg( msg );
        PyGILState_Release( gstate );
    }
}

そして、それはうまくいきます、しかし、私がそのように250以上のメッセージを送ろうとすると

for i in range(250)
    source.send('msg")

再びクラッシュします。以前と同じメッセージと症状で、

PyThreadState_Get: no current thread

そのため、今回はPythonを呼び出すのではなく、C++アプリを呼び出すときに問題が発生すると考えています。

私の質問は、boost :: pythonがpythonとのすべての相互作用のためにGIL自体を処理するようにする方法はありますか?コード内に何も見つかりません。source.send呼び出しがboost_pythonに入る場所を見つけるのは非常に困難です:(

4

3 に答える 3

4

メーリングリストで、PyEval_InitThreads()を使用するという非常にあいまいな投稿を見つけました。BOOST_PYTHON_MODULEで、それは実際にクラッシュを止めたように見えました。

プログラムが受け取ったすべてのメッセージを報告するかどうかにかかわらず、それはまだクラップスシュートです。2000を送信すると、ほとんどの場合、2000を取得したと表示されますが、レポートが大幅に少なくなることもあります。

これは、スレッドが同時にカウンターにアクセスしていることが原因である可能性があると思われるので、別の問題であるため、この質問に答えています。

修正するには

BOOST_PYTHON_MODULE(MyLib)
{
    PyEval_InitThreads();
    class_ stuff
于 2009-12-20T07:03:23.457 に答える
4

問題について正確にはわかりませんが、CallPoliciesを見てください。

http://www.boost.org/doc/libs/1_37_0/libs/python/doc/v2/CallPolicies.html#CallPolicies-concept

ラップされたC++関数の実行前および/または実行後にコードを実行する新しい呼び出しポリシー(たとえば、1つの呼び出しポリシーは「return_internal_reference」)を定義できます。C ++でラップされた関数を実行する前にGILを自動的に解放し、Pythonに戻る前に再度取得する呼び出しポリシーを正常に実装したので、次のようなコードを記述できます。

.def( "long_operation", &long_operation, release_gil<>() );

呼び出しポリシーは、このコードをより簡単に作成するのに役立つ場合があります。

于 2010-01-22T17:37:43.340 に答える
2

最善のアプローチは、GILを回避し、Pythonとの対話がシングルスレッドであることを確認することだと思います。

私は現在、boost.pythonベースのテストツールを設計しており、おそらくプロデューサー/コンシューマーキューを使用して、Pythonスレッドによって順次読み取られるマルチスレッドライブラリからイベントをディスパッチすると思います。

于 2010-08-13T08:34:06.463 に答える