C/C++からPythonの処理をマルチスレッドで使うには、C/C++側でもGILの制御が必要になる。
マルチスレッドでGILを取得せずにPythonの処理を呼び出すとメモリ例外などで異常終了する。
Pythonの仮想マシンはスレッドセーフではなくマルチスレッドでは動かせない。
マルチスレッドで動かすには、どれか一つのスレッドしか動かないようにGILと呼ばれるロックを取得する。
GILを取得しているスレッドがGILを解放すると他のスレッドが動作する。
Pythonの内部処理では、時間のかかるIO処理を行う際は、GILを解放するため、その間は他のスレッドが動作できる。
サードパーティーのライブラリでも、IO処理の際にはGILを解放するように実装されている。
C/C++からPythonの処理を呼び出す際は、GILを取得してから呼び出すようにする。
なお、シングルスレッドの場合は、GILを取得しなくても問題は起きない。
マルチスレッドで処理する際は、メインスレッドでGILを初期化しておく。
PyEval_InitThreads();
子スレッドでGILを取得するには、
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
とする。
子スレッドでPythonの呼び出しを行った後、GILを解放するには、
PyGILState_Release(gstate);
とする。
Python処理から取得したオブジェクトをC/C++側で使用する場合、Pythonのオブジェクトを使用している間は、GILを解放してはいけない。
C/C++プログラムのメインスレッドで関数の取得などを行い、
子スレッドでPythonの処理を呼び出す場合は、メインスレッドでGILを解放しておく必要がある。
取得しているGILを解放するには、
PyThreadState *_save;
_save = PyEval_SaveThread();
とする。
子スレッドの処理が終了した後、
PyEval_RestoreThread(_save);
で、GILを取得する。
参考:
初期化 (initialization)、終了処理 (finalization)、スレッド — Python 3.10.0b2 ドキュメント