스레드 상태와 전역 인터프리터 록¶
CPython 의 free-threaded build 가 아닌 경우, 파이썬 인터프리터는 일반적으로 스레드 안전(thread-safe)하지 않습니다. 다중 스레드 파이썬 프로그램을 지원하기 위해, 파이썬 객체에 접근하기 전에 스레드가 반드시 보유해야 하는 전역 록인 global interpreter lock 또는 GIL 이 존재합니다. 이 록이 없으면 아주 간단한 작업조차 다중 스레드 프로그램에서 문제를 일으킬 수 있습니다. 예를 들어, 두 개의 스레드가 동일한 객체의 참조 횟수를 동시에 증가시킬 때, 참조 횟수가 두 번 대신 한 번만 증가하는 문제가 발생할 수 있습니다.
따라서, GIL을 보유한 스레드만이 파이썬 객체를 조작하거나 파이썬의 C API를 호출할 수 있습니다.
동시성을 모방하기 위해 인터프리터는 바이트 코드 명령 사이에서 정기적으로 스레드를 전환하려고 시도합니다(sys.setswitchinterval() 참조). 이것이 순수 파이썬 코드에서 스레드 안전을 위해 록이 필요한 이유입니다.
또한, 파일 읽기나 쓰기와 같은 블로킹 I/O 작업 시에 전역 인터프리터 록이 해제됩니다. C API에서는 이를 스레드 상태 분리 를 통해 수행합니다.
The Python interpreter keeps some thread-local information inside
a data structure called PyThreadState, known as a thread state.
Each thread has a thread-local pointer to a PyThreadState; a thread state
referenced by this pointer is considered to be attached.
A thread can only have one attached thread state at a time. An attached thread state is typically analogous with holding the GIL, except on free-threaded builds. On builds with the GIL enabled, attaching a thread state will block until the GIL can be acquired. However, even on builds with the GIL disabled, it is still required to have an attached thread state, as the interpreter needs to keep track of which threads may access Python objects.
참고
자유 스레딩 빌드에서도 GIL이 다시 활성화되거나 가비지 컬렉션 동안과 같이 스레드가 일시적으로 중단될 수 있으므로 스레드 상태를 연결할 때 차단될 수 있습니다.
일반적으로 임베딩이나 메서드 구현을 포함하여 파이썬 C API를 사용할 때는 항상 연결된 스레드 상태가 존재하므로, 직접 스레드 상태를 설정해야 하는 경우는 드뭅니다. Py_BEGIN_ALLOW_THREADS 블록이나 새로운 스레드와 같은 특정 경우에만 스레드에 연결된 상태가 없을 수 있습니다. 확실하지 않은 경우, PyThreadState_GetUnchecked() 가 NULL 을 반환하는지 확인하십시오.
스레드 상태를 생성해야 하는 경우, 이를 대신 관리해 주는 PyThreadState_Ensure() 또는 PyThreadState_EnsureFromView() 를 사용하는 것이 권장됩니다.
확장 코드에서 스레드 상태 분리하기¶
Most extension code manipulating the thread state has the following simple structure:
스레드 상태를 로컬 변수에 저장합니다.
... 블로킹 I/O 작업 수행 ...
로컬 변수에서 스레드 상태를 복원합니다.
이 과정이 매우 흔하기 때문에 이를 단순화하는 한 쌍의 매크로가 존재합니다:
Py_BEGIN_ALLOW_THREADS
... 블로킹 I/O 작업 수행 ...
Py_END_ALLOW_THREADS
Py_BEGIN_ALLOW_THREADS 매크로는 새 블록을 열고 숨겨진 로컬 변수를 선언하며, Py_END_ALLOW_THREADS 매크로는 블록을 닫습니다.
위의 블록은 다음과 같은 코드로 확장됩니다:
PyThreadState *_save;
_save = PyEval_SaveThread();
... 블로킹 I/O 작업 수행 ...
PyEval_RestoreThread(_save);
이 함수들이 작동하는 방식은 다음과 같습니다.
연결된 스레드 상태는 인터프리터에 대해 GIL이 유지되고 있음을 의미합니다. 이를 분리하려면 PyEval_SaveThread() 를 호출하고 결과를 로컬 변수에 저장합니다.
스레드 상태를 분리하면 GIL이 해제되어 현재 스레드가 블로킹 I/O를 수행하는 동안 다른 스레드가 인터프리터에 연결되어 실행될 수 있습니다. I/O 작업이 완료되면 PyEval_RestoreThread() 를 호출하여 이전 스레드 상태를 다시 연결하며, 이때 GIL을 획득할 때까지 기다립니다.
참고
블로킹 I/O를 수행하는 것이 스레드 상태를 분리하는 가장 일반적인 사례이지만, 파이썬 객체나 파이썬 C API에 접근할 필요가 없는 긴 실행 시간의 네이티브 코드에서 호출하는 것도 유용합니다. 예를 들어, 표준 zlib 및 hashlib 모듈은 데이터를 압축하거나 해싱할 때 스레드 상태 를 분리합니다.
free-threaded build 에서는 GIL 이 고려 대상이 아니지만, 스레드 상태를 분리하는 것은 여전히 필요합니다. 인터프리터는 경합 조건의 위험 없이 파이썬 객체에 대한 일관된 뷰를 얻기 위해 정기적으로 모든 스레드를 차단해야 하기 때문입니다. 예를 들어, CPython은 현재 가비지 컬렉터를 실행하는 동안 짧은 시간 동안 모든 스레드를 중단합니다.
경고
스레드 상태를 분리하면 인터프리터 종료 시 예기치 않은 동작이 발생할 수 있습니다. 자세한 내용은 인터프리터 파이널리제이션 관련 주의사항 을 참조하십시오.
API¶
다음 매크로들은 일반적으로 뒤에 세미콜론 없이 사용됩니다. 파이썬 소스 배포판에서 사용 예시를 확인하십시오.
참고
이 매크로들은 데드락을 방지하기 위해 free-threaded build 에서도 여전히 필요합니다.
-
Py_BEGIN_ALLOW_THREADS¶
- …의 일부 안정 ABI.
이 매크로는
{ PyThreadState *_save; _save = PyEval_SaveThread();로 확장됩니다. 여는 중괄호가 포함되어 있으므로 뒤따르는Py_END_ALLOW_THREADS매크로와 짝을 이루어야 합니다. 이 매크로에 대한 자세한 논의는 위를 참조하십시오.
-
Py_END_ALLOW_THREADS¶
- …의 일부 안정 ABI.
이 매크로는
PyEval_RestoreThread(_save); }로 확장됩니다. 닫는 중괄호가 포함되어 있으므로 앞서 나온Py_BEGIN_ALLOW_THREADS매크로와 짝을 이루어야 합니다. 이 매크로에 대한 자세한 논의는 위를 참조하십시오.
-
Py_BLOCK_THREADS¶
- …의 일부 안정 ABI.
이 매크로는
PyEval_RestoreThread(_save);로 확장되며, 이는 닫는 중괄호가 없는Py_END_ALLOW_THREADS와 동일합니다.
-
Py_UNBLOCK_THREADS¶
- …의 일부 안정 ABI.
이 매크로는
_save = PyEval_SaveThread();로 확장되며, 이는 여는 중괄호와 변수 선언이 없는Py_BEGIN_ALLOW_THREADS와 동일합니다.
외부 스레드에서 C API 사용하기¶
전용 파이썬 API(예: threading 모듈)를 사용하여 스레드를 생성하면 스레드 상태가 자동으로 연결되지만, 네이티브 코드(예: 자체적인 스레드 관리 기능을 가진 서드파티 라이브러리 등)에서 스레드가 생성되는 경우 연결된 스레드 상태를 보유하지 않습니다.
이러한 스레드에서 파이썬 코드를 호출해야 하는 경우(종종 앞서 언급한 서드파티 라이브러리가 제공하는 콜백 API의 일부로 포함됨), 먼저 새 스레드 상태를 생성하고 이를 연결하여 해당 스레드를 인터프리터에 등록해야 합니다.
이를 수행하는 가장 쉬운 방법은 PyThreadState_Ensure() 또는 PyThreadState_EnsureFromView() 를 사용하는 것입니다.
참고
이 함수들은 대상 인터프리터를 가리키는 인자를 필요로 하며, 해당 포인터는 스레드를 생성하는 함수에서 PyInterpreterGuard_FromCurrent() (PyThreadState_Ensure 의 경우) 또는 PyInterpreterView_FromCurrent() (PyThreadState_EnsureFromView 의 경우)를 호출하여 얻을 수 있습니다. 포인터를 사용할 수 없는 경우(예: 제공된 네이티브 스레드 라이브러리가 데이터 인자를 제공하지 않는 경우), PyInterpreterView_FromMain() 을 사용하여 메인 인터프리터에 대한 뷰를 가져올 수 있으나, 이 경우 코드가 서브 인터프리터와 호환되지 않음에 유의하십시오.
예를 들어:
// 이 스레드를 생성한 함수에서 반환된 PyInterpreterGuard_FromCurrent()의 값입니다.
PyInterpreterGuard *guard = thread_data->guard;
// 인터프리터를 위한 새로운 스레드 상태를 생성합니다.
PyThreadStateToken *token = PyThreadState_Ensure(guard);
if (token == NULL) {
PyInterpreterGuard_Close(guard);
return;
}
// 유효한 스레드 상태를 확보했습니다. 여기서 파이썬 동작을 수행합니다.
result = CallSomeFunction();
// 결과를 평가하거나 예외를 처리합니다.
// 스레드 상태를 해제합니다. 이 지점 이후로는 C API를 호출할 수 없습니다.
PyThreadState_Release(token);
PyInterpreterGuard_Close(guard);
PyThreadState_Ensure 를 호출할 때 항상 새로운 스레드 상태가 생성되는 것은 아니며, PyThreadState_Release 를 호출할 때 항상 연결이 해제되는 것도 아님을 유의하십시오. 이 함수들은 이미 연결된 스레드 상태를 재사용하거나 현재 스레드에 이전에 연결되었던 스레드 상태를 다시 연결할 수 있습니다.
더 보기
반복되는 호출에 걸쳐 스레드 상태 재사용하기¶
PyThreadState 를 생성하고 파괴하는 것은 비용이 들며, 특히 free-threaded build 에서 비용이 더 많이 발생합니다. 인터프리터를 여러 번 호출하는 외부 스레드(예: 네이티브 스레드 풀의 워커 스레드)는 진입할 때마다 새로운 스레드 상태를 생성하고 종료할 때마다 파괴하는 것을 피해야 합니다. 대신, 스레드가 시작될 때(또는 파이썬을 처음 호출할 때 지연 방식으로) 하나의 스레드 상태를 설정하고, 각 호출 전후에 이를 연결(attach) 및 분리(detach)하며, 스레드가 종료될 때 단 한 번 파기해야 합니다.
PyThreadState_New() 를 사용하여 스레드 상태를 명시적으로 관리하고, PyEval_RestoreThread() 및 PyEval_SaveThread() 로 이를 연결하고 분리하십시오. 이 과정은 스레드의 생명 주기 내 서로 다른 시점에서 세 가지 뚜렷한 단계로 진행됩니다.
스레드가 시작될 때, 해당 스레드를 위한 스레드 상태를 하나 생성하십시오. interp 는 이 스레드를 생성한 코드가 연결된 스레드 상태를 유지하고 있는 동안 획득한 대상 인터프리터입니다(예: PyInterpreterState_Get() 을 통해).
PyThreadState *tstate = PyThreadState_New(interp);
그 다음, 스레드 생명 주기 동안 여러 번 발생할 수 있는 파이썬 호출 시마다 스레드 상태를 연결하고, 이를 필요로 하는 파이썬 C API 호출을 수행한 후 다시 분리하십시오. 이는 스레드가 파이썬 이외의 작업을 수행하는 동안 GIL을 보유하지 않도록 하기 위함입니다:
PyEval_RestoreThread(tstate);
result = CallSomeFunction(); /* 여기에 파이썬 C API 호출을 작성하십시오 */
PyEval_SaveThread();
스레드가 파이썬 호출을 마쳤을 때, 스레드 상태를 단 한 번 파괴하십시오:
PyEval_RestoreThread(tstate);
PyThreadState_Clear(tstate);
PyThreadState_DeleteCurrent();
외부 스레드에서 호출할 때 사용하는 범용 진입점인 PyThreadState_Ensure() 및 이전의 PyGILState_Ensure() 는 지속적인 스레드 상태를 보장하지 않습니다. 이들의 스레드 상태 생명 주기는 의도적으로 구현에 따라 정의되므로, 짝을 이루는 acquire/release 호출 시 매번 스레드 상태를 생성하고 파괴할 수 있습니다. 호출 간에 하나의 스레드 상태를 재사용하고자 할 때는 여기에 표시된 대로 PyThreadState_New() 를 사용하십시오.
외부 스레드를 생성한 코드는 스레드가 종료되기 전, 그리고 Py_FinalizeEx() 가 호출되기 전에 종료 시퀀스가 실행되도록 구성해야 합니다. 만약 인터프리터 파이널리제이션이 먼저 시작되면, 종료를 위한 PyEval_RestoreThread() 호출은 리턴하는 대신 스레드를 멈추게(hang) 됩니다(참고: 인터프리터 파이널리제이션 관련 주의사항). 만약 스레드가 종료 시퀀스를 실행하지 않고 종료되면, 프로세스가 끝날 때까지 해당 스레드 상태가 누출됩니다.
스레드 상태의 연결 및 해제¶
-
PyThreadStateToken *PyThreadState_Ensure(PyInterpreterGuard *guard)¶
- …의 일부 안정 ABI 버전 3.15 이후로.
guard 에 의해 보호되는 인터프리터에 대해 스레드가 연결된 스레드 상태를 보유하고 있음을 보장하며, 이를 통해 해당 인터프리터를 안전하게 호출할 수 있게 합니다.
스레드에 이미 연결된 스레드 상태가 있더라도, 이후에 이 호출과 짝을 이루는
PyThreadState_Release()호출이 있다면 이 함수를 호출해도 무방합니다(즉, 이 함수에 대한 “중첩된” 호출이 허용됩니다).함수의 효과(있는 경우)는 짝을 이루는
PyThreadState_Release()호출에 의해 되돌려집니다.오류 발생 시, 이 함수는 예외를 설정하지 않고
NULL을 반환합니다. 이 경우에PyThreadState_Release()를 호출하지 마십시오.성공 시, 이 함수는 짝을 이루는
PyThreadState_Release()호출에 전달되어야 하는 포인터 값을 반환합니다.The conditions in which this function creates a new thread state are considered unstable and implementation-dependent. If you need to control the exact lifetime of a thread state, consider using
PyThreadState_New(). However, do not avoid this function solely on the basis that the lifetime of the thread state may be inconsistent across versions; changes to this function will be done with caution and in a backwards-compatible manner. In particular, the saving of thread-local variables and similar state will be retained across Python versions.이 함수가 새로운 스레드 상태를 생성하는지에 대한 정확한 동작은 아래에 설명되어 있으나, 이는 향후 변경될 수 있음을 유의하십시오.
먼저, 이 함수는 연결된 스드 상태가 존재하는지 확인합니다. 존재한다면, 해당 스레드 상태의 인터프리터가 guard 에 의해 보호되는 인터프리터와 일치하는지 확인합니다. 일치하는 경우, 이 함수는 해당 스레드 상태를
PyThreadState_Ensure호출에 의해 사용 중으로 표시하고 반환합니다.연결된 스레드 상태가 없는 경우, 이 함수는 현재 OS 스레드에 의해 사용된 적이 있는 스레드 상태가 있는지 확인합니다. (이는
PyGILState_GetThisThreadState()에 의해 반환됩니다.) 만약 있다면, 해당 스레드 상태의 인터프리터가 guard 와 일치하는지 확인합니다. 일치한다면, 이를 다시 연결하고 사용 중으로 표시합니다.그렇지 않고 위의 두 경우 모두 실패하면, guard 를 위해 새로운 스레드 상태가 생성됩니다. 그 후 연결되고
PyThreadState_Ensure에 의해 소유됨으로 표시됩니다.Added in version 3.15.
-
PyThreadStateToken *PyThreadState_EnsureFromView(PyInterpreterView *view)¶
- …의 일부 안정 ABI 버전 3.15 이후로.
view 가 참조하는 인터프리터에 대해 연결된 스레드 상태를 가져옵니다.
동작 및 반환 값은
PyThreadState_Ensure()와 동일합니다. 추가로 함수가 성공하면 view 가 참조하는 인터프리터가 암시적으로 보호됩니다. 이 보호는 대응하는PyThreadState_Release()호출 시 해제됩니다.Added in version 3.15.
-
void PyThreadState_Release(PyThreadStateToken *token)¶
- …의 일부 안정 ABI 버전 3.15 이후로.
PyThreadState_Ensure()또는PyThreadState_EnsureFromView()호출을 취소합니다.이 함수는 성공적인 Ensure 호출마다 정확히 한 번 호출되어야 하며, token 은 해당 호출의 반환 값으로 설정되어야 합니다.
PyThreadState_Release()가 반환될 때, 대응하는 Ensure 호출 전에 연결되었던 상태(있는 경우)가 다시 연결됩니다.이 함수가 스레드 상태를 삭제하는지에 대한 정확한 동작은 불안정하며 구현에 따라 다를 수 있습니다.
현재 이 함수는 첨부된 스레드 상태의 내부 카운터를 감소시킵니다. 이 카운터가 0 미만으로 내려가면, 이 함수는 치명적인 오류(
Py_FatalError()\를 통해)를 발생시킵니다.연결된 스레드 상태가
PyThreadState_Ensure에 의해 소유된 경우, 내부 카운트가 0이 되면 해당 스레드 상태가 해제 및 삭제됩니다. 그렇지 않은 경우에는 카운트가 0이 되어도 아무 일도 일어나지 않습니다.Added in version 3.15.
-
type PyThreadStateToken¶
- …의 일부 안정 ABI (불투명 구조체로) 버전 3.15 이후로.
PyThreadState_Ensure()호출에서 가져와 대응하는PyThreadState_Release()호출에 전달되는 불투명한 토큰입니다.
GIL 상태 API¶
다음 API들은 일반적으로 서브 인터프리터와 호환되지 않으며, 인터프리터 파이널라이제이션 중에 프로세스를 중단시킵니다(상세 내용은 인터프리터 파이널리제이션 관련 주의사항 참조). 따라서 이러한 API들은 Python 3.15에서 새 API 를 대신하기 위해 soft deprecated 되었습니다.
-
type PyGILState_STATE¶
- …의 일부 안정 ABI.
PyGILState_Ensure()에서 반환되어PyGILState_Release()에 전달되는 값의 타입입니다.-
enumerator PyGILState_LOCKED¶
PyGILState_Ensure()가 호출될 때 이미 GIL이 보유된 상태였습니다.
-
enumerator PyGILState_UNLOCKED¶
PyGILState_Ensure()가 호출될 때 GIL이 보유되지 않은 상태였습니다.
-
enumerator PyGILState_LOCKED¶
-
PyGILState_STATE PyGILState_Ensure()¶
- …의 일부 안정 ABI.
현재 Python의 상태나 attached thread state\와 관계없이 현재 스레드가 Python C API를 호출할 준비가 되었음을 보장합니다. 이 함수는 각 호출이
PyGILState_Release()\ 호출과 일치하는 한, 스레드에서 원하는 만큼 여러 번 호출될 수 있습니다. 일반적으로 다른 스레드 관련 API는 Release() 이전의 이전 상태로 스레드 상태를 복원하기만 한다면,PyGILState_Ensure()\와PyGILState_Release()\ 호출 사이에 사용할 수 있습니다. 예를 들어,Py_BEGIN_ALLOW_THREADS\ 및Py_END_ALLOW_THREADS\ 마크로의 정상적인 사용은 허용됩니다.반환 값은
PyGILState_Ensure()\가 호출되었을 때의 attached thread state \에 대한 불투명한 “핸들”이며, Python이 동일한 상태로 남겨지도록 하려면PyGILState_Release()\에 전달해야 합니다. 재귀적 호출은 허용되지만, 이 핸들은 공유 될 수 없습니다.PyGILState_Ensure()\의 각 고유한 호출은 해당 호출의PyGILState_Release()\를 위한 핸들을 저장해야 합니다.함수가 반환되면, attached thread state\가 존재하며 스레드는 임의의 Python 코드를 호출할 수 있게 됩니다.
이 함수는 에러를 반환할 방법이 없습니다. 따라서 오류가 발생하면 치명적(즉,
SIGABERT를 보내고 프로세스를 중단시킴;Py_FatalError()참조)이거나, 스레드가 영구적으로 차단됩니다(인터프리터 파이널라이제이션 중 등).경고
인터프리터가 파이널라이제이션될 때 이 함수를 호출하면 스레드가 무한히 멈추고 교착 상태가 발생할 수 있습니다. 자세한 내용은 인터프리터 파이널리제이션 관련 주의사항 을 참조하십시오.
또한, 이 함수는 외부 스레드에서 사용될 때 일반적으로 서브 인터프리터와 호환되지 않습니다. 이 함수는 어떤 인터프리터가 스레드를 생성했는지 알 수 있는 방법이 없기 때문입니다(따라서 암시적으로 메인 인터프리터를 선택하게 됩니다).
버전 3.14에서 변경: 인터프리터가 파이널라이제이션되는 동안 호출되면 현재 스레드를 종료하는 대신 중단시킵니다.
버전 3.15부터 약하게 폐지 <Soft deprecated>: 대신
PyThreadState_Ensure()또는PyThreadState_EnsureFromView()를 사용하십시오.
-
void PyGILState_Release(PyGILState_STATE)¶
- …의 일부 안정 ABI.
이전에 획득한 모든 자원을 해제합니다. 이 호출 이후 파이썬의 상태는 대응하는
PyGILState_Ensure()호출 이전과 동일하게 됩니다(그러나 일반적으로 이 상태는 호출자에게 알려지지 않으므로, GIL-state API를 사용합니다).PyGILState_Ensure()에 대한 모든 호출은 동일한 스레드에서PyGILState_Release()와 짝을 이루어 호출되어야 합니다.버전 3.15부터 약하게 폐지 <Soft deprecated>: 대신
PyThreadState_Release()를 사용하십시오.
-
PyThreadState *PyGILState_GetThisThreadState()¶
- …의 일부 안정 ABI.
이 스레드에 가장 최근에 attached 로 thread state 데이터를 가져옵니다. (최근의 스레드 상태가 삭제된 경우, 이 값은
NULL을 반환합니다.)호출자가 연결된 스레드 상태를 가지고 있는 경우 이를 반환합니다.
In other terms, this function returns the thread state that will be used by
PyGILState_Ensure(). If this returnsNULL, thenPyGILState_Ensurewill create a new thread state.이 함수는 실패할 수 없습니다.
버전 3.15부터 약하게 폐지 <Soft deprecated>: 대신
PyThreadState_Get()또는PyThreadState_GetUnchecked()를 사용하십시오.
-
int PyGILState_Check()¶
현재 스레드가
PyGILState_GetThisThreadState()에서 반환된 스레드 상태와 일치하는 attached thread state 를 가지고 있다면1을 반환합니다. 호출자에게 첨부된 스레드 상태가 없거나 다르게 일치하지 않으면, 이는0을 반환합니다.현재 파이썬 프로세스에서 한 번이라도 서브 인터프리터를 생성한 적이 있다면, 이 함수는 항상
1을 반환합니다.이것은 주로 도우미 또는 진단용 기능입니다.
Added in version 3.4.
버전 3.15부터 약하게 폐지 <Soft deprecated>: 대신
PyThreadState_GetUnchecked() != NULL을 사용하십시오.
fork()에 관한 주의사항¶
스레드와 관련하여 주목해야 할 또 다른 중요한 사항은 C fork() 호출 시의 동작입니다. fork() 가 있는 대부분의 시스템에서 프로세스가 포크된 후에는 fork를 실행한 스레드만 존재하게 됩니다. 이는 록(lock) 처리 방식과 CPython 런타임에 저장된 모든 상태에 구체적인 영향을 미칩니다.
“현재” 스레드만 남는다는 것은 다른 스레드가 보유한 록이 절대 해제되지 않음을 의미합니다. 파이썬은 os.fork() 에서 이를 해결하기 위해 내부적으로 사용하는 록을 포크 전에 획득하고 포크 후에 해제하며, 자식 프로세스에서 모든 Lock 객체 를 재설정합니다. 파이썬을 확장하거나 내장할 때, 포크 전이나 후에 획득 또는 재설정해야 하는 추가적인 (파이썬 외부) 록에 대해 파이썬에 알릴 방법은 없습니다. 동일한 목적을 달성하려면 pthread_atfork() 와 같은 OS 기능을 사용해야 합니다. 또한, 파이썬을 확장하거나 내장할 때 os.fork() 를 통하지 않고 fork() 를 직접 호출하면(그리고 파이썬으로 돌아오거나 호출하는 경우), 포크 후 사라진 스레드가 파이썬의 내부 록 중 하나를 보유하고 있어 교착 상태가 발생할 수 있습니다. PyOS_AfterFork_Child() 는 필요한 록을 재설정하려고 시도하지만, 항상 성공하는 것은 아닙니다.
다른 모든 스레드가 사라진다는 것은 또한 CPython의 런타임 상태가 적절하게 정리되어야 함을 의미하며, os.fork() 는 이를 수행합니다. 이는 현재 인터프리터에 속한 다른 모든 PyThreadState 객체와 다른 모든 PyInterpreterState 객체를 파이널라이제이션 하는 것을 의미합니다. 이러한 점과 “메인” 인터프리터 의 특수한 성질로 인해, fork() 는 CPython 전역 런타임이 처음으로 초기화된 해당 인터프리터의 “메인” 스레드에서만 호출되어야 합니다. 유일한 예외는 exec() 가 직후에 호출되는 경우뿐입니다.
고수준 API¶
다음은 멀티스레드 C 확장을 작성할 때 가장 흔히 사용되는 타입과 함수들입니다.
-
type PyThreadState¶
- …의 일부 안정 ABI (불투명 구조체로).
이 데이터 구조는 단일 스레드의 상태를 나타냅니다. 유일한 공개 데이터 멤버는 다음과 같습니다.
-
PyInterpreterState *interp¶
해당 스레드의 인터프리터 상태입니다.
-
PyInterpreterState *interp¶
-
void PyEval_InitThreads()¶
- …의 일부 안정 ABI.
아무것도 수행하지 않는 폐지된 함수입니다.
Python 3.6 이하 버전에서 이 함수는 존재하지 않을 경우 GIL을 생성했습니다.
버전 3.9에서 변경: 이제 이 함수는 아무것도 수행하지 않습니다.
버전 3.7에서 변경: 이 함수는 이제
Py_Initialize()에 의해 호출되므로, 더 이상 직접 호출할 필요가 없습니다.버전 3.2에서 변경: This function cannot be called before
Py_Initialize()anymore.버전 3.9부터 폐지됨.
-
PyThreadState *PyEval_SaveThread()¶
- …의 일부 안정 ABI.
Detach the attached thread state and return it. The thread will have no thread state upon returning.
-
void PyEval_RestoreThread(PyThreadState *tstate)¶
- …의 일부 안정 ABI.
Set the attached thread state to tstate. The passed thread state should not be attached, otherwise deadlock ensues. tstate will be attached upon returning.
참고
런타임이 파이널라이제이션될 때 스레드에서 이 함수를 호출하면, 해당 스레드가 파이썬에 의해 생성되지 않았더라도 프로그램이 종료될 때까지 스레드가 중단됩니다. 자세한 내용은 인터프리터 파이널리제이션 관련 주의사항 을 참조하십시오.
버전 3.14에서 변경: 인터프리터가 파이널라이제이션되는 동안 호출되면 현재 스레드를 종료하는 대신 중단시킵니다.
-
PyThreadState *PyThreadState_Get()¶
- …의 일부 안정 ABI.
Return the attached thread state. If the thread has no attached thread state, (such as when inside of
Py_BEGIN_ALLOW_THREADSblock), then this issues a fatal error (so that the caller needn’t check forNULL).PyThreadState_GetUnchecked()도 참조하십시오.
-
PyThreadState *PyThreadState_GetUnchecked()¶
PyThreadState_Get()과 유사하지만, 결과가 NULL인 경우 치명적인 오류로 프로세스를 종료하지 않습니다. 결과가 NULL인지 확인하는 것은 호출자의 책임입니다.Added in version 3.13: Python 3.5에서 3.12까지 이 함수는 비공개였으며
_PyThreadState_UncheckedGet()으로 알려져 있었습니다.
-
PyThreadState *PyThreadState_Swap(PyThreadState *tstate)¶
- …의 일부 안정 ABI.
Set the attached thread state to tstate, and return the thread state that was attached prior to calling.
This function is safe to call without an attached thread state; it will simply return
NULLindicating that there was no prior thread state.참고
PyGILState_Ensure()와 유사하게, 이 함수는 런타임이 파이널라이즈되는 동안 스레드를 중단시킵니다.
저수준 API¶
-
PyThreadState *PyThreadState_New(PyInterpreterState *interp)¶
- …의 일부 안정 ABI.
주어진 인터프리터 객체에 속하는 새로운 스레드 상태 객체를 생성합니다. 첨부된 스레드 상태 가 필요하지 않습니다.
-
void PyThreadState_Clear(PyThreadState *tstate)¶
- …의 일부 안정 ABI.
스레드 상태 객체의 모든 정보를 재설정합니다. tstate 는 반드시 첨부된 스레드 상태 여야 합니다.
버전 3.9에서 변경: 이 함수는 이제
PyThreadState.on_delete콜백을 호출합니다. 이전에는 이 과정이PyThreadState_Delete()에서 수행되었습니다.버전 3.13에서 변경:
PyThreadState.on_delete콜백이 제거되었습니다.
-
void PyThreadState_Delete(PyThreadState *tstate)¶
- …의 일부 안정 ABI.
스레드 상태 객체를 파괴합니다. tstate 는 어떤 스레드에도 첨부된 스레드 상태 상태여서는 안 됩니다. tstate 는 이전에
PyThreadState_Clear()를 호출하여 재설정되어야 합니다.
-
void PyThreadState_DeleteCurrent(void)¶
첨부된 스레드 상태 (이전의
PyThreadState_Clear()호출로 재설정되어야 함)를 분리한 다음 파괴합니다.함수가 반환될 때 어떤 스레드 상태 도 첨부된 스레드 상태 가 되지 않습니다.
-
PyFrameObject *PyThreadState_GetFrame(PyThreadState *tstate)¶
- …의 일부 안정 ABI 버전 3.10 이후로.
파이썬 스레드 상태 tstate 의 현재 프레임을 가져옵니다.
강한 참조 를 반환합니다. 현재 실행 중인 프레임이 없으면
NULL을 반환합니다.PyEval_GetFrame()도 참조하십시오.tstate 는
NULL이 아니어야 하며, 반드시 첨부된 스레드 상태 여야 합니다.Added in version 3.9.
-
uint64_t PyThreadState_GetID(PyThreadState *tstate)¶
- …의 일부 안정 ABI 버전 3.10 이후로.
파이썬 스레드 상태 tstate 의 고유한 스레드 상태 식별자를 가져옵니다.
tstate 는
NULL이 아니어야 하며, 반드시 첨부된 스레드 상태 여야 합니다.Added in version 3.9.
-
PyInterpreterState *PyThreadState_GetInterpreter(PyThreadState *tstate)¶
- …의 일부 안정 ABI 버전 3.10 이후로.
파이썬 스레드 상태 tstate 의 인터프리터를 가져옵니다.
tstate 는
NULL이 아니어야 하며, 반드시 첨부된 스레드 상태 여야 합니다.Added in version 3.9.
-
void PyThreadState_EnterTracing(PyThreadState *tstate)¶
파이썬 스레드 상태 tstate 에서 트레이싱 및 프로파일링을 중단합니다.
PyThreadState_LeaveTracing()함수를 사용하여 재개합니다.Added in version 3.11.
-
void PyThreadState_LeaveTracing(PyThreadState *tstate)¶
PyThreadState_EnterTracing()함수에 의해 중단된 파이썬 스레드 상태 tstate 의 트레이싱 및 프로파일링을 재개합니다.PyEval_SetTrace()및PyEval_SetProfile()함수도 참조하십시오.Added in version 3.11.
-
int PyUnstable_ThreadState_SetStackProtection(PyThreadState *tstate, void *stack_start_addr, size_t stack_size)¶
- 이것은 불안정 API. 마이너 릴리스에서 예고 없이 변경될 수 있습니다.
파이썬 스레드 상태의 스택 보호 시작 주소와 스택 보호 크기를 설정합니다.
성공 시
0을 반환합니다. 실패 시 예외를 설정하고-1을 반환합니다.CPython은 기계 실행 스택이 넘침에 가까워지면
RecursionError를 발생시켜 C 코드에 대한 재귀 제어 를 구현합니다. 예로Py_EnterRecursiveCall()함수를 참조하십시오. 이를 위해 시스템은 현재 스레드 스택의 위치를 알아야 하며, 보통 운영체제로부터 이 정보를 얻습니다. Boost 라이브러리의boost::context와 같은 컨텍스트 전환 기술을 사용하여 스택이 변경될 때,PyUnstable_ThreadState_SetStackProtection()을 호출하여 CPython에 변경 사항을 알려야 합니다.스택을 변경하기 전이나 후에
PyUnstable_ThreadState_SetStackProtection()을 호출하십시오. 해당 호출과 스택 변경 사이에 다른 파이썬 C API를 호출하지 마십시오.이 작업을 취소하려면
PyUnstable_ThreadState_ResetStackProtection()을 참조하십시오.Added in version 3.15.
-
void PyUnstable_ThreadState_ResetStackProtection(PyThreadState *tstate)¶
- 이것은 불안정 API. 마이너 릴리스에서 예고 없이 변경될 수 있습니다.
파이썬 스레드 상태의 스택 보호 시작 주소와 스택 보호 크기를 운영체제 기본값으로 재설정합니다.
설명은
PyUnstable_ThreadState_SetStackProtection()을 참조하십시오.Added in version 3.15.
-
PyObject *PyThreadState_GetDict()¶
- 반환값: 빌린 참조. …의 일부 안정 ABI.
확장 프로그램이 스레드 특정 상태 정보를 저장할 수 있는 딕셔너리를 반환합니다. 각 확장 프로그램은 딕셔너리에 상태를 저장하기 위해 고유한 키를 사용해야 합니다. 스레드 상태 가 첨부된 상태 가 아닐 때도 이 함수를 호출할 수 있습니다. 이 함수가
NULL을 반환하고 예외가 발생하지 않았다면, 호출자는 스레드 상태가 첨부되지 않았다고 가정해야 합니다.
-
void PyEval_AcquireThread(PyThreadState *tstate)¶
- …의 일부 안정 ABI.
현재 스레드에 *tstate*를 첨부하십시오. tstate 는
NULL이 아니어야 하며 이미 첨부된 상태 여서는 안 됩니다.호출하는 스레드는 이미 첨부된 스레드 상태 를 가지고 있지 않아야 합니다.
참고
런타임이 파이널라이제이션될 때 스레드에서 이 함수를 호출하면, 해당 스레드가 파이썬에 의해 생성되지 않았더라도 프로그램이 종료될 때까지 스레드가 중단됩니다. 자세한 내용은 인터프리터 파이널리제이션 관련 주의사항 을 참조하십시오.
버전 3.8에서 변경:
PyEval_RestoreThread(),Py_END_ALLOW_THREADS(), 그리고PyGILState_Ensure()와 일관되도록 업데이트되었으며, 인터프리터가 파이널라이즈되는 동안 호출되면 현재 스레드를 종료합니다.버전 3.14에서 변경: 인터프리터가 파이널라이제이션되는 동안 호출되면 현재 스레드를 종료하는 대신 중단시킵니다.
PyEval_RestoreThread()은 (스레드가 초기화되지 않았을 때조차) 항상 사용 가능한 고수준 함수입니다.
-
void PyEval_ReleaseThread(PyThreadState *tstate)¶
- …의 일부 안정 ABI.
첨부된 스레드 상태 를 분리합니다.
NULL이 아니어야 하는 tstate 인자는 해당 값이 첨부된 스레드 상태 를 나타내는지 확인하는 용도로만 사용되며, 그렇지 않을 경우 치명적인 오류가 보고됩니다.PyEval_SaveThread()은 (스레드가 초기화되지 않았을 때조차) 항상 사용 가능한 고수준 함수입니다.
비동기 알림¶
메인 인터프리터 스레드에 비동기 알림을 보내는 메커니즘이 제공됩니다. 이러한 알림은 함수 포인터와 void 포인터 인수의 형태를 가집니다.
-
int Py_AddPendingCall(int (*func)(void*), void *arg)¶
- …의 일부 안정 ABI.
메인 인터프리터 스레드에서 호출할 함수를 예약합니다. 성공 시
0이 반환되고 func 은 메인 스레드에서 실행되도록 큐에 추가됩니다. 실패 시 예외 설정 없이-1이 반환됩니다.성공적으로 큐에 추가되면, func 은 결국 메인 인터프리터 스레드에서 인자 arg 와 함께 호출됩니다. 정상적으로 실행되는 파이썬 코드와 비교하여 비동기적으로 호출되지만, 다음 두 조건이 모두 충족된 상태에서 실행됩니다:
바이트 코드 경계에서;
메인 스레드가 첨부된 스레드 상태 를 보유한 상태에서 수행됩니다. (따라서 func 은 전체 C API를 사용할 수 있습니다.)
func 은 성공 시
0을 반환해야 하며, 실패 시 예외를 설정하고-1을 반환해야 합니다. func 이 재귀적으로 다른 비동기 알림을 수행하기 위해 중단되지는 않지만, 스레드 상태 가 분리된 경우 스레드를 전환하기 위해 중단될 수는 있습니다.이 함수는 첨부된 스레드 상태 가 필요하지 않습니다. 그러나 서브 인터프리터에서 이 함수를 호출하려면 호출자가 첨부된 스레드 상태 를 보유해야 합니다. 그렇지 않으면 func 이 잘못된 인터프리터에서 실행되도록 예약될 수 있습니다.
경고
이것은 매우 특수한 경우에만 유용한 저수준 함수입니다. func 가 가능한 한 빨리 호출된다는 보장은 없습니다. 메인 스레드가 시스템 호출을 실행 중이면, 시스템 호출이 반환되기 전에 func 이 호출되지 않습니다. 이 함수는 일반적으로 임의의 C 스레드에서 파이썬 코드를 호출하는 데 적합하지 않습니다. 대신
PyThreadState_EnsureFromView()를 사용하십시오.Added in version 3.1.
버전 3.9에서 변경: 이 함수가 서브 인터프리터에서 호출되면, func 은 메인 인터프리터 대신 해당 서브 인터프리터에서 호출되도록 예약됩니다. 이제 각 서브 인터프리터는 자체적인 예약된 호출 목록을 가집니다.
버전 3.12에서 변경: 이 함수는 이제 항상 func 이 메인 인터프리터에서 실행되도록 예약합니다.
-
int Py_MakePendingCalls(void)¶
- …의 일부 안정 ABI.
모든 대기 중인 호출을 실행합니다. 이 작업은 일반적으로 인터프리터에 의해 자동으로 수행됩니다.
이 함수는 성공 시
0을 반환하고, 실패 시 예외를 설정하며-1을 반환합니다.메인 인터프리터의 메인 스레드에서 호출되지 않으면, 이 함수는 아무 작업도 하지 않고
0을 반환합니다. 호출자는 첨부된 스레드 상태 를 보유해야 합니다.Added in version 3.1.
버전 3.12에서 변경: 이 함수는 메인 인터프리터에서 대기 중인 호출만 실행합니다.
-
int PyThreadState_SetAsyncExc(unsigned long id, PyObject *exc)¶
- …의 일부 안정 ABI.
스레드에서 비동기적으로 발생할 예외를 예약합니다. 해당 스레드에 이전에 예약된 예외가 있는 경우 덮어씌워집니다.
id 인자는
PyThread_get_thread_ident()에서 반환되는 대상 스레드의 ID입니다. exc 는 발생시킬 예외의 클래스이며, 대기 중인 예외가 있는 경우 이를 제거하려면NULL을 전달합니다.영향을 받은 스레드 상태의 수를 반환합니다. id 를 찾은 경우, 변경 사항이 없더라도(제공된 exc 가 이미 대기 중이거나, exc 가
NULL이고 대기 중인 예외가 없는 경우 포함) 보통1을 반환합니다. 스레드 ID를 찾지 못한 경우0을 반환합니다. 이 함수는 예외를 발생시키지 않습니다.부주의한 오용을 방지하기 위해, 이 함수를 호출하는 전용 C 확장을 작성해야 합니다. 이 함수는 반드시 첨부된 스레드 상태 와 함께 호출되어야 합니다. 이 함수는 exc 에 대한 어떤 참조도 탈취하지 않습니다. 또한 이 함수가
sleep()과 같은 시스템 호출을 반드시 중단시키는 것은 아닙니다.버전 3.7에서 변경: id 파라미터의 타입이 long 에서 unsigned long 으로 변경되었습니다.
운영체제 스레드 API¶
-
PYTHREAD_INVALID_THREAD_ID¶
유효하지 않은 스레드 ID를 위한 센티널 값입니다.
현재 이 값은
(unsigned long)-1과 동일합니다.
-
unsigned long PyThread_start_new_thread(void (*func)(void*), void *arg)¶
- …의 일부 안정 ABI.
인자 arg 와 함께 새로운 스레드에서 함수 func 을 시작합니다. 생성된 스레드는 join될 목적이 아닙니다.
func 은
NULL이 아니어야 하지만, arg 는NULL일 수 있습니다.성공 시 이 함수는 새 스레드의 식별자를 반환하며, 실패 시
PYTHREAD_INVALID_THREAD_ID를 반환합니다.호출자가 첨부된 스레드 상태 를 보유할 필요가 없습니다.
-
unsigned long PyThread_get_thread_ident(void)¶
- …의 일부 안정 ABI.
결코 0이 되지 않는 현재 스레드의 식별자를 반환합니다.
이 함수는 실패할 수 없으며, 호출자가 첨부된 스레드 상태 를 보유할 필요가 없습니다.
더 보기
threading.get_ident()및threading.Thread.ident이 이 식별자를 파이썬에 노출합니다.
-
PyObject *PyThread_GetInfo(void)¶
- …의 일부 안정 ABI 버전 3.3 이후로.
현재 스레드에 대한 일반 정보를 구조체 시퀀스 객체 형태로 가져옵니다. 이 정보는 파이썬에서
sys.thread_info로 접근할 수 있습니다.성공 시 이 함수는 스레드 정보에 대한 새로운 강한 참조 를 반환하며, 실패 시 예외를 설정하고
NULL을 반환합니다.호출자는 첨부된 스레드 상태 를 보유해야 합니다.
-
PY_HAVE_THREAD_NATIVE_ID¶
이 매크로는 시스템이 네이티브 스레드 ID를 지원할 때 정의됩니다.
-
unsigned long PyThread_get_thread_native_id(void)¶
- …의 일부 안정 ABI on platforms with native thread IDs.
운영체제 커널이 할당한 현재 스레드의 네이티브 식별자를 가져옵니다. 이 값은 0보다 작지 않습니다.
이 함수는
PY_HAVE_THREAD_NATIVE_ID가 정의된 경우에만 사용할 수 있습니다.이 함수는 실패할 수 없으며, 호출자가 첨부된 스레드 상태 를 보유할 필요가 없습니다.
-
void PyThread_exit_thread(void)¶
- …의 일부 안정 ABI.
현재 스레드를 종료합니다. 이 함수는 일반적으로 안전하지 않은 것으로 간주되므로 피해야 합니다. 오직 하위 호환성을 위해서만 유지됩니다.
이 함수는 전체 호출 스택에 있는 모든 함수가 이를 안전하게 허용하도록 작성된 경우에만 호출하는 것이 안전합니다.
경고
현재 시스템이 POSIX 스레드(pthreads로도 알려짐)를 사용하는 경우 이 함수는 pthread_exit(3)`을 호출하며, 이는 일부 libc 구현에서 스택을 풀고 C++ 소멸자를 호출하려고 시도합니다. 그러나 ``noexcept` 함수에 도달하면 프로세스가 종료될 수 있습니다. macOS와 같은 다른 시스템에서는 언와인딩(unwinding)이 수행됩니다.
Windows에서 이 함수는
_endthreadex()를 호출하며, 이는 C++ 소멸자를 호출하지 않고 스레드를 종료합니다.어떠한 경우라도 스레드의 스택이 손상될 위험이 있습니다.
버전 3.14부터 폐지됨.
-
void PyThread_init_thread(void)¶
- …의 일부 안정 ABI.
PyThread*API를 초기화합니다. 파이썬이 이 함수를 자동으로 실행하므로 확장 모듈에서 호출할 필요는 거의 없습니다.
-
int PyThread_set_stacksize(size_t size)¶
- …의 일부 안정 ABI.
현재 스레드의 스택 크기를 size 바이트로 설정합니다.
이 함수는 성공 시
0을 반환하고, size 가 유효하지 않으면-1을, 시스템이 스택 크기 변경을 지원하지 않으면-2를 반환합니다. 이 함수는 예외를 설정하지 않습니다.호출자가 첨부된 스레드 상태 를 보유할 필요가 없습니다.
-
size_t PyThread_get_stacksize(void)¶
- …의 일부 안정 ABI.
현재 스레드의 스택 크기를 바이트 단위로 반환하며, 시스템의 기본 스택 크기가 사용 중인 경우
0을 반환합니다.호출자가 첨부된 스레드 상태 를 보유할 필요가 없습니다.