#
8.1 スレッド状態 (thread state) とグローバルインタプリタロック (global interpreter lock)

Python インタプリタは完全にスレッド安全 (thread safe) ではありません。 マルチスレッドの Python プログラムをサポートするために、グローバルな ロックが存在していて、現在のスレッドが Python オブジェクトに安全に アクセスする前に必ずロックを獲得しなければならなくなっています。 ロック機構がなければ、単純な操作でさえ、マルチスレッドプログラムの 実行に問題を引き起こす可能性があります: たとえば、二つのスレッドが 同じオブジェクトの参照カウントを同時にインクリメントすると、 結果的に参照カウントは二回でなく一回だけしかインクリメントされない かもしれません。

このため、グローバルインタプリタロックを獲得したスレッドだけが Python オブジェクトを操作したり、 Python/C API 関数を呼び出したり できるというルールがあります。マルチスレッドの Python プログラムを サポートするため、インタプリタは定期的に -- デフォルトの設定では バイトコード 100 命令ごとに (この値は `sys.setcheckinterval()` で 変更できます) -- ロックを解放したり獲得したりします。 このロックはブロックが起こりうる I/O 操作の付近でも解放・獲得 され、I/O を要求するスレッドが I/O 操作の完了を待つ間、他の スレッドが動作できるようにしています。

Python インタプリタはスレッドごとに何らかの予約情報を持っておかねば なりません -- このため、Python は `PyThreadState` と呼ばれるデータ構造 を用います。これは Python 1.5 からの仕様です; 以前のバージョンでは、 スレッド状態はグローバル変数内に保存されていて、スレッドを切り替えると 問題を起こすことがありました。特に、アプリケーションが `sys.exc_info()` を使って、現在のスレッドで送出された 最新の例外にアクセスする際の例外処理は、いまやスレッド安全になりました。 とはいえ、グローバル変数はまだ一つだけ残っています: それは現在の `PyThreadState` 構造体を指すポインタです。 ほとんどのスレッドパッケージが ``スレッドごとのグローバルデータ'' を保存する手段を持っている一方で、Python の内部的なプラットフォーム 非依存のスレッド抽象層はこれをサポートしていません。従って、 現在のスレッド状態を明示的に操作するようにしなければなりません。

ほとんどのケースで、このような操作は十分簡単にできます。 グローバルインタプリタロックを操作数ほとんどのコードは、以下のような 単純な構造を持ちます:

    スレッド状態をローカル変数に保存する。
    インタプリタロックを解放する。
    ...ブロックが起きるような何らかの I/O 操作...
    インタプリタロックを獲得する。
    ローカル変数からスレッド状態を回復する。

このやりかたは非常に一般的なので、作業を単純にするために二つの マクロが用意されています:

    Py_BEGIN_ALLOW_THREADS
    ...ブロックが起きるような何らかの I/O 操作...
    Py_END_ALLOW_THREADS

Py_BEGIN_ALLOW_THREADS マクロは新たなブロック文を開始し、隠しローカル変数を宣言します; Py_END_ALLOW_THREADS はブロック文を終了します。これらの二つのマクロを使うもうひとつの 利点は、Python をスレッドサポートなしでコンパイルしたとき、 マクロの内容、すなわちスレッド状態の退避とロック操作が空になると いう点です。

スレッドサポートが有効になっている場合、上記のブロックは 以下のようなコードに展開されます:

        PyThreadState *_save;

        _save = PyEval_SaveThread();
        ...ブロックが起きるような何らかの I/O 操作...
        PyEval_RestoreThread(_save);

より低水準のプリミティブを使うと、以下のようにしてほぼ同じ効果を 得られます:

        PyThreadState *_save;

        _save = PyThreadState_Swap(NULL);
        PyEval_ReleaseLock();
        ...ブロックが起きるような何らかの I/O 操作...
        PyEval_AcquireLock();
        PyThreadState_Swap(_save);

上の二つには微妙な違いがあります; とりわけ、 `PyEval_RestoreThread()` はグローバル変数 `errno` の値を保存しておいて 元に戻す点が異なります。というのは、ロック操作が `errno` に 何もしないという保証がないからです。また、スレッドサポートが無効化 されている場合、 `PyEval_SaveThread()` および `PyEval_RestoreThread()` はロックを操作しません; この場合、 `PyEval_ReleaseLock()` および `PyEval_AcquireLock()` は 利用できません。この仕様は、スレッドサポートを無効化してコンパイル されているインタプリタが、スレッドサポートが有効化された状態で コンパイルされている動的ロード拡張モジュールをロードできる ようにするためのものです。

グローバルインタプリタロックは、現在のスレッド状態を指すポインタを 保護するために使われます。ロックを解放してスレッド状態を退避する際、 ロックを解放する前に現在のスレッド状態ポインタを取得しておかなければ なりません (他のスレッドがすぐさまロックを獲得して、自らの スレッド状態をグローバル変数に保存してしまうかもしれないからです)。 逆に、ロックを獲得してスレッド状態を復帰する際には、 グローバル変数にスレッド状態ポインタを保存する前にロックを獲得して おかなければなりません。

なぜここまで詳しく説明しようとするかおわかりでしょうか? それは、 C でスレッドを生成した場合、そのスレッドにはグローバルインタプリタ ロックがなく、スレッド状態データ構造体もないからです。このような スレッドが Python/C API を利用するには、まずスレッド状態データ構造体を 生成し、次にロックを獲得し、そしてスレッド状態ポインタを保存すると いったように、自分自身をブートストラップして生成しなければ なりません。スレッドが作業を終えたら、スレッド状態ポインタを リセットして、ロックを解放し、最後にスレッド状態データ構造体を メモリ解放しなければなりません。

スレッドデータ構造体を生成する際には、インタプリタ状態データ構造体を 指定する必要があります。インタプリタ状態データ構造体は、 インタプリタ内の全てのスレッド間で共有されているグローバルなデータ、 例えばモジュール管理データ (codesys.modules) を保持しています。 必要に応じて、新たなインタプリタ状態データ構造体を作成するなり、 Python メインスレッドが使っているインタプリタ状態データ構造体 を共有するなりできます (後者のデータにアクセスするためには、 スレッド状態データ構造体を獲得して、その `interp` メンバ にアクセスしなければなりません; この処理は、Python が作成した スレッドから行うか、Python を初期化した後で主スレッドから行わねば なりません)。

インタプリタオブジェクトにアクセスできるという仮定の下では、C の スレッドから Python を呼び出す際の典型的な常套句は以下のようになります。

        PyThreadState *tstate;
        PyObject *result;

        /* interp is your reference to an interpreter object. */
        tstate = PyThreadState_New(interp);
        PyEval_AcquireThread(tstate);

        /* Perform Python actions here.  */
        result = CallSomeFunction();
        /* evaluate result */

        /* Release the thread. No Python API allowed beyond this point. */
        PyEval_ReleaseThread(tstate);

        /* You can either delete the thread state, or save it
           until you need it the next time. */
        PyThreadState_Delete(tstate);

`PyInterpreterState`
  このデータ構造体は、協調動作する多数のスレッド間で共有されている 状態 (state) を表現します。同じインタプリタに属するスレッドは モジュール管理情報やその他いくつかの内部的な情報を共有しています。 この構造体には公開 (public) のメンバはありません。

  異なるインタプリタに属するスレッド間では、利用可能なメモリ、 開かれているファイルデスクリプタなどといったプロセス状態を除き、 初期状態では何も共有されていません。グローバルインタプリタロック もまた、スレッドがどのインタプリタに属しているかに関わらず すべてのスレッドで共有されています。

`PyThreadState`
  単一のスレッドの状態を表現する表現するデータ構造体です。 データメンバ `PyInterpreterState *``interp` だけが公開されていて、スレッドのインタプリタ状態を指すポインタに なっています。

void `PyEval_InitThreads`( | )

  グローバルインタプリタロックを初期化し、獲得します。 この関数は、主スレッドが第二のスレッドを生成する以前や、 `PyEval_ReleaseLock()` や `PyEval_ReleaseThread(tstate)` といった他のスレッド操作に入るよりも前に呼び出されるようにして おかなければなりません。

  二度目に呼び出すと何も行いません。この関数を `Py_Initialize()` の前に呼び出しても 安全です。

  主スレッドしか存在しないのであれば、ロック操作は必要ありません。 これはよくある状況ですし (ほとんどの Python プログラムはスレッドを 使いません)、ロック操作はインタプリタをごくわずかに低速化します。 従って、初期状態ではロックは生成されません。ロックを使わない状況は、 すでにロックを獲得している状況と同じです: 単一のスレッドしか なければ、オブジェクトへのアクセスは全て安全です。従って、 この関数がロックを初期化すると、同時にロックを獲得するようになって います。Python の `thread` モジュールは、 新たなスレッドを作成する前に、ロックが存在するか、あるいはまだ 作成されていないかを調べ、`PyEval_InitThreads()` を 呼び出します。この関数から処理が戻った場合、ロックの作成と獲得が 保証されています。

  どのスレッドが現在グローバルインタプリタロックを (存在する場合) 持っているか分からない時にこの関数を使うのは安全では ありません 。

  この関数はコンパイル時にスレッドサポートを無効化すると利用できません。

void `PyEval_AcquireLock`( | )

  グローバルインタプリタロックを獲得します。 ロックは前もって作成されていなければなりません。 この関数を呼び出したスレッドがすでにロックを獲得している場合、 デッドロックに陥ります。 この関数はコンパイル時にスレッドサポートを無効化すると利用できません。

void `PyEval_ReleaseLock`( | )

  グローバルインタプリタロックを解放します。 ロックは前もって作成されていなければなりません。 この関数はコンパイル時にスレッドサポートを無効化すると利用できません。

void `PyEval_AcquireThread`( | PyThreadState *tstate)

  グローバルインタプリタロックを獲得し、現在のスレッド状態を tstate に設定します。tstate は `NULL`であっては なりません。ロックはあらかじめ作成されていなければなりません。 この関数を呼び出したスレッドがすでにロックを獲得している場合、 デッドロックに陥ります。 この関数はコンパイル時にスレッドサポートを無効化すると利用できません。

void `PyEval_ReleaseThread`( | PyThreadState *tstate)

  現在のスレッド状態をリセットして `NULL` にし、グローバルインタプリタ ロックを解放します。ロックはあらかじめ作成されていなければならず、 かつ現在のスレッドが保持していなければなりません。tstate は `NULL`であってはなりませんが、その値が現在のスレッド状態を 表現しているかどうかを調べるためにだけ使われます -- もしそうで なければ、致命的エラーが報告されます。 この関数はコンパイル時にスレッドサポートを無効化すると利用できません。

PyThreadState* `PyEval_SaveThread`( | )

  (インタプリタロックが生成されていて、スレッドサポートが有効の 場合) インタプリタロックを解放して、スレッド状態を `NULL`にし、 以前のスレッド状態 (`NULL`にはなりません) を返します。 ロックがすでに生成されている場合、現在のスレッドがロックを獲得 していなければなりません。

void `PyEval_RestoreThread`( | PyThreadState *tstate)

  (インタプリタロックが生成されていて、スレッドサポートが有効の 場合) インタプリタロックを獲得して、現在のスレッド状態を tstate に設定します。tstate は `NULL`であっては なりません。 この関数を呼び出したスレッドがすでにロックを獲得している場合、 デッドロックに陥ります。 (この関数はコンパイル時にスレッドサポートを無効化すると利用できません。)

以下のマクロは、通常末尾にセミコロンを付けずに使います; Python ソース配布物内の使用例を見てください。

`Py_BEGIN_ALLOW_THREADS`
  このマクロを展開すると "`{ PyThreadState *_save; _save = PyEval_SaveThread();`"になります。 マクロに開き波括弧が入っていることに注意してください; この波括弧は 後で Py_END_ALLOW_THREADS マクロと対応させなければ なりません。 マクロについての詳しい議論は上記を参照してください。 コンパイル時にスレッドサポートが無効化されていると何も行いません。

`Py_END_ALLOW_THREADS`
  このマクロを展開すると "`PyEval_RestoreThread(_save); }`"になります。 マクロに開き波括弧が入っていることに注意してください; この波括弧は 事前の Py_BEGIN_ALLOW_THREADS マクロと対応して いなければなりません。 マクロについての詳しい議論は上記を参照してください。 コンパイル時にスレッドサポートが無効化されていると何も行いません。

`Py_BLOCK_THREADS`
  このマクロを展開すると "`PyEval_RestoreThread(_save);`"になります: 閉じ波括弧のないPy_END_ALLOW_THREADS と同じです。 コンパイル時にスレッドサポートが無効化されていると何も行いません。

`Py_UNBLOCK_THREADS`
  このマクロを展開すると "`_save = PyEval_SaveThread();`"になります: 閉じ波括弧のないPy_BEGIN_ALLOW_THREADS と同じです。 コンパイル時にスレッドサポートが無効化されていると何も行いません。

以下の全ての関数はコンパイル時にスレッドサポートが有効になっている 時だけ利用でき、呼び出すのはインタプリタロックがすでに作成されている 場合だけにしなくてはなりません。

PyInterpreterState* `PyInterpreterState_New`( | )

  新しいインタプリタ状態オブジェクトを生成します。 インタプリタロックを保持しておく必要はありませんが、この関数を次々に 呼び出す必要がある場合には保持しておいたほうがよいでしょう。

void `PyInterpreterState_Clear`( | PyInterpreterState *interp)

  インタプリタ状態オブジェクト内の全ての情報をリセットします。 インタプリタロックを保持していなければなりません。

void `PyInterpreterState_Delete`( | PyInterpreterState *interp)

  インタプリタ状態オブジェクトを破壊します。 インタプリタロックを保持しておく必要はありません。 インタプリタ状態は`PyInterpreterState_Clear()` であらかじめ リセットしておかなければなりません。

PyThreadState* `PyThreadState_New`( | PyInterpreterState *interp)

  指定したインタプリタオブジェクトに属する新たなスレッド状態オブジェクトを 生成します。 インタプリタロックを保持しておく必要はありませんが、この関数を次々に 呼び出す必要がある場合には保持しておいたほうがよいでしょう。

void `PyThreadState_Clear`( | PyThreadState *tstate)

  スレッド状態オブジェクト内の全ての情報をリセットします。 インタプリタロックを保持していなければなりません。

void `PyThreadState_Delete`( | PyThreadState *tstate)

  スレッド状態オブジェクトを破壊します。 インタプリタロックを保持していなければなりません。 スレッド状態は`PyThreadState_Clear()` であらかじめ リセットしておかなければなりません。

PyThreadState* `PyThreadState_Get`( | )

  現在のスレッド状態を返します。 インタプリタロックを保持していなければなりません。 現在のスレッド状態が `NULL`なら、(呼び出し側が `NULL`チェックを しなくてすむように) この関数は致命的エラーを起こすようになっています。

PyThreadState* `PyThreadState_Swap`( | PyThreadState *tstate)

  現在のスレッド状態を tstate に指定したスレッド状態と入れ変えます。 tstate は`NULL`であってはなりません。 インタプリタロックを保持していなければなりません。

PyObject* `PyThreadState_GetDict`( | )

  戻り値: 借りた参照.
  拡張モジュールがスレッド固有の状態情報を保存できるような辞書を返します。 各々の拡張モジュールが辞書に状態情報を保存するためには唯一のキーを 使わねばなりません。 現在のスレッド状態がない時にこの関数を呼び出してもかまいません。 この関数が `NULL`を返す場合、例外はまったく送出されず、呼び出し側は 現在のスレッド状態が利用できないと考えねばなりません。 バージョン2.3 以降で変更された 仕様: 以前は、現在のスレッドがアクティブなときのみ呼び出せる ようになっており、 `NULL` は例外が送出されたことを意味していました

int `PyThreadState_SetAsyncExc`( | long id, PyObject *exc)

  スレッド内で非同期的に例外を送出します。 id 引数はターゲットとなるスレッドのスレッド id です; exc は送出する例外オブジェクトです。 この関数は exc に対する参照を一切盗み取りません。 素朴な間違いを防ぐため、この関数を呼び出すには独自に C 拡張モジュール を書かねばなりません。 グローバルインタプリタロックを保持した状態で呼び出さなければなりません。 変更を受けたスレッド状態の数を返します; 1 よりも大きな数を返した場合、 何らかのトラブルに巻き込まれていることになり、exc を `NULL` にして再度呼び出すことで効果を打ち消さねばなりません。 この関数自体は例外を送出しません。 バージョン2.3 以降で新規追加された 仕様です。