동기화 프리미티브¶
C-API는 기본적인 상호 배제(mutual exclusion) 록을 제공합니다.
-
type PyMutex¶
상호 배제(mutual exclusion) 록입니다.
PyMutex은 잠금 해제 상태를 나타내기 위해 0으로 초기화되어야 합니다. 예:PyMutex mutex = {0};
PyMutex인스턴스는 복사하거나 이동해서는 안 됩니다.PyMutex의 내용과 주소 모두 의미가 있으며, 고정된 쓰기 가능한 메모리 위치에 있어야 합니다.참고
PyMutex는 현재 1바이트를 차지하지만, 크기는 불안정한 것으로 간주해야 합니다. 향후 파이썬 버전에서 폐지 예고 기간 없이 크기가 변경될 수 있습니다.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을 반환합니다.
참고
이 함수는 단지 확인(assertion) 및 디버깅 용도로만 설계되었으며, 체크 직후에 록 상태가 바뀔 수 있으므로 동시성 제어를 위한 결정에 사용해서는 안 됩니다.
Added in version 3.14.
파이썬 크리티컬 섹션(critical section) API¶
The critical section API provides a deadlock avoidance layer on top of per-object locks for free-threaded CPython. They are intended to replace reliance on the global interpreter lock, and are no-ops in versions of Python with the global interpreter lock.
Critical sections are intended to be used for custom types implemented
in C-API extensions. They should generally not be used with built-in types like
list and dict because their public C-APIs
already use critical sections internally, with the notable
exception of PyDict_Next(), which requires critical section
to be acquired externally.
크리티컬 섹션은 활성화된 크리티컬 섹션을 암시적으로 중단함으로써 데드록을 방지하므로, PyMutex 와 같은 전통적인 록이 제공하는 독점적 접근 권한을 제공하지 않습니다. 크리티컬 섹션이 시작될 때 해당 객체의 개체별 록이 확보됩니다. 만약 크리티컬 섹션 내부에서 실행되는 코드가 C-API 함수를 호출하면, 해당 크리티컬 섹션을 중단하여 개체별 록을 해제할 수 있으며, 이를 통해 다른 스레드가 동일한 객체에 대한 개체별 록을 획득할 수 있습니다.
파이썬 객체 대신 PyMutex 포인터를 수용하는 변형(variant)도 제공됩니다. PyObject 가 없는 상황—예를 들어, PyObject 를 확장하거나 래핑하지 않으면서 데드락을 유발할 가능성이 있는 방식으로 C API를 호출해야 하는 C 타입과 작업하는 경우—에 크리티컬 섹션을 시작하려면 이러한 변형을 사용하십시오.
참고
동시에 두 객체를 잠가야 하는 작업은 반드시 Py_BEGIN_CRITICAL_SECTION2 를 사용해야 합니다. 내부 크리티컬 섹션이 외부 크리티컬 섹션을 중단시킬 수 있으므로, 중첩된 크리티컬 섹션을 사용하여 동시에 둘 이상의 객체를 잠그는 것은 불가능 합니다. 이 API는 세 개 이상의 객체를 동시에 잠그는 방법을 제공하지 않습니다.
사용 예시:
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 를 호출하며, 이는 객체의 해제 함수를 통해 임의의 코드를 실행할 수 있습니다. 크리티컬 섹션 API는 파이널라이저에 의해 트리거된 코드가 블록되어 PyEval_SaveThread() 를 호출하는 경우, 런타임이 일시적으로 크리티컬 섹션을 중단할 수 있게 함으로써 재진입성 및 잠금 순서로 인한 잠재적인 데드락을 방지합니다.
-
Py_BEGIN_CRITICAL_SECTION(op)¶
- …의 일부 안정 ABI 버전 3.15 이후로.
객체 op 에 대한 개별 객체 잠금을 획득하고 크리티컬 섹션을 시작합니다.
free-threaded 빌드 및 Stable ABI 를 위해 빌드할 때, 이 매크로는 다음과 같이 확장됩니다:
{ PyCriticalSection _py_cs; PyCriticalSection_Begin(&_py_cs, (PyObject*)(op))
기본 빌드에서 이 매크로는 다음과 같이 확장됩니다.
{.Added in version 3.13.
-
Py_BEGIN_CRITICAL_SECTION_MUTEX(m)¶
뮤텍스 m 을 잠그고 크리티컬 섹션을 시작합니다.
free-threaded 빌드에서 이 매크로는 다음과 같이 확장됩니다:
{ PyCriticalSection _py_cs; PyCriticalSection_BeginMutex(&_py_cs, m)
Py_BEGIN_CRITICAL_SECTION과 달리, 이 매크로의 인자에 대한 캐스팅이 없으며 반드시PyMutex포인터여야 함에 유의하십시오.기본 빌드에서 이 매크로는 다음과 같이 확장됩니다.
{.Added in version 3.14.
-
Py_END_CRITICAL_SECTION()¶
- …의 일부 안정 ABI 버전 3.15 이후로.
크리티컬 섹션을 종료하고 개별 객체 잠금을 해제합니다.
free-threaded 빌드 및 Stable ABI 를 위해 빌드할 때, 이 매크로는 다음과 같이 확장됩니다:
PyCriticalSection_End(&_py_cs); }
기본 빌드에서 이 매크로는 다음과 같이 확장됩니다.
}.Added in version 3.13.
-
Py_BEGIN_CRITICAL_SECTION2(a, b)¶
- …의 일부 안정 ABI 버전 3.15 이후로.
객체 a 와 b 에 대한 개별 객체 잠금을 획득하고 크리티컬 섹션을 시작합니다. 잠금 순서로 인한 데드락을 방지하기 위해 두 잠금은 일관된 순서(낮은 주소부터)로 획득됩니다.
free-threaded 빌드에서 이 매크로는 다음과 같이 확장됩니다:
{ 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 를 잠그고 크리티컬 섹션을 시작합니다.
free-threaded 빌드 및 Stable ABI 를 위해 빌드할 때, 이 매크로는 다음과 같이 확장됩니다:
{ PyCriticalSection2 _py_cs2; PyCriticalSection2_BeginMutex(&_py_cs2, m1, m2)
Py_BEGIN_CRITICAL_SECTION2와 달리, 이 매크로의 인자에 대한 캐스팅이 없으며 반드시PyMutex포인터여야 함에 유의하십시오.기본 빌드에서 이 매크로는 다음과 같이 확장됩니다.
{.Added in version 3.14.
-
Py_END_CRITICAL_SECTION2()¶
- …의 일부 안정 ABI 버전 3.15 이후로.
크리티컬 섹션을 종료하고 개별 객체 잠금을 해제합니다.
free-threaded 빌드 및 Stable ABI 를 위해 빌드할 때, 이 매크로는 다음과 같이 확장됩니다:
PyCriticalSection2_End(&_py_cs2); }
기본 빌드에서 이 매크로는 다음과 같이 확장됩니다.
}.Added in version 3.13.
저수준 크리티컬 섹션 API¶
C 매크로를 사용할 수 없는 경우를 위해 다음 함수와 구조체가 노출됩니다.
-
void PyCriticalSection_Begin(PyCriticalSection *c, PyObject *op)¶
-
void PyCriticalSection_End(PyCriticalSection *c)¶
-
void PyCriticalSection2_Begin(PyCriticalSection2 *c, PyObject *a, PyObject *b)¶
-
void PyCriticalSection2_End(PyCriticalSection2 *c);¶
- …의 일부 안정 ABI 버전 3.15 이후로.
이 기능은 단지 이 섹션 앞부분에 나열된 의 매크로 확장과 동일한 경우에만 사용되어야 합니다.
CPython의 비-머지된(non-) free-threaded 빌드에서 이 함수들은 아무것도 수행하지 않습니다.
Added in version 3.13.
-
type PyCriticalSection¶
-
type PyCriticalSection2¶
- …의 일부 안정 ABI (모든 멤버 포함) 버전 3.15 이후로.
이 기능은 단지 이 섹션 앞부분에 나열된 의 매크로 확장과 동일한 경우에만 사용되어야 합니다. 구조체의 내용은 비공개이며 향후 파이썬 버전에서 그 의미가 변경될 수 있음에 유의하십시오.
Added in version 3.13.
-
void PyCriticalSection_BeginMutex(PyCriticalSection *c, PyMutex *m);¶
-
void PyCriticalSection2_BeginMutex(PyCriticalSection2 *c, PyMutex *m1, PyMutex *m2);¶
이 기능은 단지 이 섹션 앞부분에 나열된 의 매크로 확장과 동일한 경우에만 사용되어야 합니다.
CPython의 비-머지된(non-) free-threaded 빌드에서 이 함수들은 아무것도 수행하지 않습니다.
Added in version 3.14.
레거시 잠금 API¶
이 API들은 PyMutex 가 도입된 파이썬 3.13부터 구식(obsolete)입니다.
버전 3.15에서 변경: 이 API들은 이제 PyMutex 를 단순하게 래핑한 것입니다.
-
type PyThread_type_lock¶
상호 배제(mutual exclusion) 록에 대한 포인터입니다.
-
type PyLockStatus¶
타임아웃을 사용하여 록을 획득한 결과입니다.
-
enumerator PY_LOCK_FAILURE¶
록 획득에 실패했습니다.
-
enumerator PY_LOCK_ACQUIRED¶
록이 성공적으로 획득되었습니다.
-
enumerator PY_LOCK_INTR¶
시그널에 의해 록 획득이 중단되었습니다.
-
enumerator PY_LOCK_FAILURE¶
-
PyThread_type_lock PyThread_allocate_lock(void)¶
- …의 일부 안정 ABI.
새 록을 할당합니다.
성공 시 이 함수는 록을 반환하고, 실패 시 예외를 설정하지 않고
0을 반환합니다.호출자가 첨부된 스레드 상태 를 보유할 필요가 없습니다.
버전 3.15에서 변경: 이 함수는 이제 항상
PyMutex를 사용합니다. 이전 버전에서는 운영체제에서 제공하는 록을 사용했습니다.
-
void PyThread_free_lock(PyThread_type_lock lock)¶
- …의 일부 안정 ABI.
lock 을 파괴합니다. 이 함수를 호출할 때 해당 록은 어떤 스레드도 보유하고 있어서는 안 됩니다.
호출자가 첨부된 스레드 상태 를 보유할 필요가 없습니다.
-
PyLockStatus PyThread_acquire_lock_timed(PyThread_type_lock lock, long long microseconds, int intr_flag)¶
- …의 일부 안정 ABI.
타임아웃을 설정하여 lock 을 획득합니다.
이 함수는 록을 획득하기 위해 microseconds 마이크로초 동안 대기합니다. 타임아웃이 만료되면 이 함수는
PY_LOCK_FAILURE를 반환합니다. microseconds 가-1인 경우, 록이 해제될 때까지 무기한으로 기다립니다.intr_flag 가
1인 경우, 록 획득이 시그널에 의해 중단될 수 있으며, 이 경우 함수는PY_LOCK_INTR를 반환합니다. 중단 시 호출자는 일반적으로 파이썬 코드에 예외를 전파하기 위해Py_MakePendingCalls()를 호출해야 합니다.록을 성공적으로 획득한 경우 이 함수는
PY_LOCK_ACQUIRED를 반환합니다.호출자가 첨부된 스레드 상태 를 보유할 필요가 없습니다.
-
int PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)¶
- …의 일부 안정 ABI.
lock 을 획득합니다.
waitflag 이
1이고 다른 스레드가 현재 록을 보유하고 있는 경우, 이 함수는 록을 획득할 수 있을 때까지 대기하며 항상1을 반환합니다.waitflag 이
0이고 다른 스레드가 록을 보유하고 있는 경우, 이 함수는 기다리지 않고 대신0을 반환합니다. 어떤 다른 스레드도 록을 보유하고 있지 않은 경우, 이 함수는 록을 획득하고1을 반환합니다.PyThread_acquire_lock_timed()와 달리, 록 획득은 시그널에 의해 중단될 수 없습니다.호출자가 첨부된 스레드 상태 를 보유할 필요가 없습니다.
-
int PyThread_release_lock(PyThread_type_lock lock)¶
- …의 일부 안정 ABI.
lock 을 해제합니다. lock 이 보유되지 않은 상태에서 이 함수를 호출하면 치명적인 오류가 발생합니다.
호출자가 첨부된 스레드 상태 를 보유할 필요가 없습니다.