同期プリミティブ

C API は基本的な相互排他ロックを提供します。

type PyMutex

相互排他ロックです。 PyMutex は、ロックされていない状態を表すために 0 で初期化する必要があります。 例えば次のようにします。

PyMutex mutex = {0};

PyMutex のインスタンスは、コピーしたり移動したりしてはなりません。 PyMutex は内容とアドレスの両方が意味を持つため、書き込み可能なメモリ上の固定された場所に置き続ける必要があります。

注釈

PyMutex は現在 1 バイトを占めますが、このサイズは安定した仕様ではありません。 将来の Python リリースでは、非推奨の期間を設けずにサイズを変更する可能性があります。

Added in version 3.13.

void PyMutex_Lock(PyMutex *m)
Thread safety: Atomic.

ミューテックス m をロックします。 別のスレッドがすでにロックしている場合、呼び出し元のスレッドはロックが解除されるまでブロックします。 ブロック中は、スレッド状態 があれば一時的にデタッチします。

Added in version 3.13.

void PyMutex_Unlock(PyMutex *m)
Thread safety: Atomic.

ミューテックス m のロックを解除します。 ミューテックスはロックされていなければなりません。 ロックされていない場合、この関数は致命的なエラーを発生させます。

Added in version 3.13.

int PyMutex_IsLocked(PyMutex *m)
Thread safety: Atomic.

ミューテックス m が現在ロックされていれば 0 以外を、そうでなければ 0 を返します。

注釈

この関数はアサーションとデバッグだけを目的としています。 確認した直後にロックの状態が変わる可能性があるため、並行処理の制御の判断には使わないでください。

Added in version 3.14.

Python のクリティカルセクション API

クリティカルセクション API は、フリースレッドの CPython のオブジェクトごとのロックに、デッドロックを避ける層を加えます。 グローバルインタープリタロック への依存を置き換えるためのもので、グローバルインタープリタロックを持つバージョンの Python では何もしません。

クリティカルセクションは、C API を使う拡張で実装した独自の型のためのものです。 list や dict などの組み込み型の公開 C API は内部でクリティカルセクションを使っているため、通常はこれらの型に対して使わないでください。 重要な例外は PyDict_Next() で、外側でクリティカルセクションを開始しておく必要があります。

クリティカルセクションは、実行中のクリティカルセクションを暗黙的に中断することでデッドロックを避けます。 そのため、PyMutex のような従来のロックが提供する排他的アクセスとは異なります。 クリティカルセクションを開始すると、対象のオブジェクトのロックを取得します。 内部で実行するコードが C API 関数を呼ぶと、クリティカルセクションを中断してそのロックを解放する場合があります。 すると、別のスレッドが同じオブジェクトのロックを取得できます。

Python オブジェクトの代わりに PyMutex ポインタを受け取る形式もあります。 PyObject がない状況でクリティカルセクションを開始する場合に使ってください。 例えば、PyObject を拡張もラップもしていない C の型を扱うものの、デッドロックを起こす可能性がある形で C API を呼び出す必要がある場合です。

The functions and structs used by the macros are exposed for cases where C macros are not available. They should only be used as in the given macro expansions. Note that the sizes and contents of the structures may change in future Python versions.

注釈

2 個のオブジェクトを同時にロックする操作には、Py_BEGIN_CRITICAL_SECTION2 を使う必要があります。 複数のオブジェクトを同時にロックするために、クリティカルセクションを入れ子にすることは できません。 内側のクリティカルセクションが外側のものを中断する場合があるためです。 この API は、3 個以上のオブジェクトを同時にロックする方法を提供しません。

使用例:

static PyObject *
set_field(MyObject *self, PyObject *value)
{
   Py_BEGIN_CRITICAL_SECTION(self);
   Py_SETREF(self->field, Py_XNewRef(value));
   Py_END_CRITICAL_SECTION();
   Py_RETURN_NONE;
}

上の例では、Py_SETREF が Py_DECREF を呼び出し、オブジェクトの解放関数を通じて任意のコードが実行される可能性があります。 ファイナライザが実行するコードがブロックして PyEval_SaveThread() を呼び出した場合、 クリティカルセクション API はランタイムによるクリティカルセクションの一時的な中断を許すことで、再入やロックの順序に起因するデッドロックを避けます。

Py_BEGIN_CRITICAL_SECTION(op)

オブジェクト op のロックを取得し、クリティカルセクションを開始します。

フリースレッドビルドでは、このマクロは次のように展開されます。

{
    PyCriticalSection _py_cs;
    PyCriticalSection_Begin(&_py_cs, (PyObject*)(op))

デフォルトのビルドでは、このマクロは { に展開されます。

Added in version 3.13.

Py_BEGIN_CRITICAL_SECTION_MUTEX(m)

ミューテックス m をロックし、クリティカルセクションを開始します。

フリースレッドビルドでは、このマクロは次のように展開されます。

{
     PyCriticalSection _py_cs;
     PyCriticalSection_BeginMutex(&_py_cs, m)

Py_BEGIN_CRITICAL_SECTION と異なり、このマクロは引数をキャストしません。 引数は PyMutex ポインタでなければなりません。

デフォルトのビルドでは、このマクロは { に展開されます。

Added in version 3.14.

Py_END_CRITICAL_SECTION()

クリティカルセクションを終了し、オブジェクトのロックを解放します。

フリースレッドビルドでは、このマクロは次のように展開されます。

    PyCriticalSection_End(&_py_cs);
}

デフォルトのビルドでは、このマクロは } に展開されます。

Added in version 3.13.

Py_BEGIN_CRITICAL_SECTION2(a, b)

オブジェクト a と b のロックを取得し、クリティカルセクションを開始します。 ロックの順序によるデッドロックを避けるため、常に同じ順序 (アドレスが小さいものから) でロックを取得します。

フリースレッドビルドでは、このマクロは次のように展開されます。

{
    PyCriticalSection2 _py_cs2;
    PyCriticalSection2_Begin(&_py_cs2, (PyObject*)(a), (PyObject*)(b))

デフォルトのビルドでは、このマクロは { に展開されます。

Added in version 3.13.

Py_BEGIN_CRITICAL_SECTION2_MUTEX(m1, m2)

ミューテックス m1 と m2 をロックし、クリティカルセクションを開始します。

フリースレッドビルドでは、このマクロは次のように展開されます。

{
     PyCriticalSection2 _py_cs2;
     PyCriticalSection2_BeginMutex(&_py_cs2, m1, m2)

Py_BEGIN_CRITICAL_SECTION2 と異なり、このマクロは引数をキャストしません。 引数は PyMutex ポインタでなければなりません。

デフォルトのビルドでは、このマクロは { に展開されます。

Added in version 3.14.

Py_END_CRITICAL_SECTION2()

クリティカルセクションを終了し、オブジェクトのロックを解放します。

フリースレッドビルドでは、このマクロは次のように展開されます。

    PyCriticalSection2_End(&_py_cs2);
}

デフォルトのビルドでは、このマクロは } に展開されます。

Added in version 3.13.

従来のロック API

これらの API は、Python 3.13 で PyMutex が導入されたことにより旧式となりました。

type PyThread_type_lock

相互排他ロックへのポインタです。

PY_TIMEOUT_T

A soft deprecated alias to long long, the integer type used for the timeout argument of PyThread_acquire_lock_timed().

バージョン 3.14.10 で ソフト非推奨 <Soft deprecated>.

const long long PY_TIMEOUT_MAX

The maximum timeout, in microseconds, supported by PyThread_acquire_lock_timed().

注釈

This differs from _thread.TIMEOUT_MAX, which is expressed in seconds.

type PyLockStatus

タイムアウト付きでロックを取得した結果です。

enumerator PY_LOCK_FAILURE

ロックを取得できませんでした。

enumerator PY_LOCK_ACQUIRED

ロックを取得しました。

enumerator PY_LOCK_INTR

ロックの取得がシグナルで中断されました。

PyThread_type_lock PyThread_allocate_lock(void)
次に属します: Stable ABI.

新しいロックを確保します。

成功するとロックを返します。 失敗すると、例外を設定せずに 0 を返します。

呼び出し元に スレッド状態がアタッチされている 必要はありません。

void PyThread_free_lock(PyThread_type_lock lock)
次に属します: Stable ABI.

lock を破棄します。 呼び出す際に、どのスレッドもこのロックを保持していてはなりません。

呼び出し元に スレッド状態がアタッチされている 必要はありません。

PyLockStatus PyThread_acquire_lock_timed(PyThread_type_lock lock, long long microseconds, int intr_flag)
次に属します: Stable ABI.

タイムアウト付きで lock を取得します。

ロックを取得するまで microseconds マイクロ秒待ちます。 タイムアウトすると PY_LOCK_FAILURE を返します。 microseconds が -1 の場合は、ロックが解放されるまで無期限に待ちます。

intr_flag が 1 の場合、ロックの取得がシグナルで中断されることがあります。 その場合は PY_LOCK_INTR を返します。 中断されたときは、通常、呼び出し元が Py_MakePendingCalls() を呼んで Python コードに例外を伝えることが想定されています。

ロックの取得に成功すると PY_LOCK_ACQUIRED を返します。

呼び出し元に スレッド状態がアタッチされている 必要はありません。

int PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)
次に属します: Stable ABI.

lock を取得します。

waitflag が 1 で、別のスレッドが現在ロックを保持している場合、ロックを取得できるまで待ち、常に 1 を返します。

waitflag が 0 で、別のスレッドがロックを保持している場合は、待たずに 0 を返します。 他のスレッドがロックを保持していなければ、ロックを取得して 1 を返します。

PyThread_acquire_lock_timed() と異なり、ロックの取得がシグナルで中断されることはありません。

呼び出し元に スレッド状態がアタッチされている 必要はありません。

int PyThread_release_lock(PyThread_type_lock lock)
次に属します: Stable ABI.

lock を解放します。 lock が保持されていない場合、この関数は致命的なエラーを発生させます。

呼び出し元に スレッド状態がアタッチされている 必要はありません。