組み込みPythonの停止
-
07-07-2019 - |
質問
PythonインタープリターをCプログラムに埋め込みます。ただし、 PyRun_SimpleString()
を介して一部のpythonスクリプトを実行しているときに、無限ループに陥ったり、実行時間が長すぎたりすることがあります。 PyRun_SimpleString(" while 1:pass");
を検討してください。メインプログラムがブロックするのを防ぐために、スレッドでインタープリターを実行できると考えました。
プロセス全体を強制終了せずに、スレッドで実行されている組み込みインタープリターでpythonスクリプトの実行を停止するにはどうすればよいですか?
インタプリタに例外を渡すことは可能ですか?シグナルをリッスンする他のスクリプトでスクリプトをラップする必要がありますか?
PS:別のプロセスでpythonを実行することもできますが、これは私が望むものではありません-最後の手段でない限り...
更新:
だから、今は動作します。デニス・オトキダッハ、ありがとう!
これが正しい場合は、2つのことを行う必要があります。PyRun_SimpleString()が実行されているのと同じスレッドでインタープリターに停止することと return -1
を指示します。
停止するために、いくつかの可能性があります: PyErr_SetString(PyExc_KeyboardInterrupt、" ...")
または PyErr_SetInterrupt()
-最初のPythonさらにいくつかの命令を実行すると停止し、後の命令はすぐに実行を停止します。
return -1
には、 Py_AddPendingCall()
を使用して、Python実行に関数呼び出しを注入します。ドキュメントではバージョン2.7および3.1以降に言及していますが、以前のPython(ここでは2.6)でも実行されます。 2.7および3.1からはスレッドセーフでもある必要があります。つまり、GIL(?)を取得せずに呼び出すことができます。
したがって、以下の例を書き換えることができます。
int quit() {
PyErr_SetInterrupt();
return -1;
}
解決
Py_AddPendingCall()
を使用して、次のチェック間隔で呼び出される例外を発生させる関数を追加できます(詳細については、 sys.setcheckinterval()
のドキュメントを参照してください)。以下は Py_Exit()
呼び出しの例です(私にとってはうまくいきますが、おそらく必要ではありません)。それを Py_Finalize()
または PyErr_Set *()
:
int quit(void *) {
Py_Exit(0);
}
PyGILState_STATE state = PyGILState_Ensure();
Py_AddPendingCall(&quit, NULL);
PyGILState_Release(state);
これは、純粋なPythonコードには十分なはずです。ただし、一部のC関数はしばらくの間単一の操作として実行できることに注意してください(正規表現検索を長時間実行する例がありましたが、まだ関連があるかはわかりません)。
他のヒント
さて、Pythonインタープリターは埋め込みプログラムとは別のスレッドで実行する必要があります。そうしないと、インタープリターを中断する機会がまったくなくなります。
それがあれば、おそらくPython API例外呼び出しの1つを使用して、インタープリターで例外をトリガーできますか? Python C API例外
をご覧ください。無限ループや長時間実行されるコードのキャンセルなど、アクティブに実行中のPythonコードブロックを中断できるようにしたいと考えました。私のアプリケーションでは、シングルスレッドのPythonコードが送信され、評価されます。割り込み要求はいつでも受信できます。この質問と回答は、私が実際にそれを達成するのを助けるために重要でした。
2019年にこの質問を見ると、ここでの解決策はうまくいきませんでした。さまざまな方法で Py_AddPendingCall(& quit、NULL);
を使用しようとしましたが、呼び出された関数が期待どおりに実行されなかったか、まったく実行されませんでした。これにより、 Py_FinalizeEx()
を送信したときにPythonエンジンがハングし、最終的にクラッシュしました。この非常に便利な同様の質問への回答で必要なものがようやく見つかりました。
Pythonエンジンを起動した後、Pythonコードの threading
パッケージを使用してスレッドIDを取得します。それをc long値に解析して保存します。
割り込み関数は実際には非常に簡単です:
PyGILState_Ensure()
PyThreadState_SetAsyncExc(threadId、PyExc_Exception)
以前に保存したスレッドIDと一般的な例外タイプを使用して
PyGILState_Release()