파이썬 프로세스 내의 여러 인터프리터¶
대부분의 경우 단일 파이썬 인터프리터를 임베딩하지만, 동일한 프로세스 또는 심지어 동일한 스레드 내에서 여러 개의 독립적인 인터프리터를 생성해야 하는 경우가 있습니다. 서브 인터프리터를 통해 이를 수행할 수 있습니다.
“main” 인터프리터는 런타임이 초기화될 때 생성되는 첫 번째 인터프리터입니다. 일반적으로 프로세스 내에 존재하는 유일한 파이썬 인터프리터입니다. 서브 인터프리터와 달리 메인 인터프리터는 시그널 처리와 같은 고유한 프로세스 전역적 책임을 가집니다. 또한 런타임 초기화 동안 실행을 담당하며, 일반적으로 런타임 종료 시 활성 인터프리터가 됩니다. PyInterpreterState_Main() 함수는 해당 상태에 대한 포인터를 반환합니다.
PyThreadState_Swap() 함수를 사용하여 서브 인터프리터 간을 전환할 수 있습니다. 다음 함수들을 사용하여 서브 인터프리터를 생성하고 파괴할 수 있습니다.
-
type PyInterpreterConfig¶
서브 인터프리터를 구성하는 대부분의 매개변수를 포함하는 구조체입니다. 해당 값들은
Py_NewInterpreterFromConfig()에서만 사용되며, 런타임에 의해 수정되지 않습니다.Added in version 3.12.
구조체 필드:
-
int use_main_obmalloc¶
이 값이
0이면 서브 인터프리터는 자체 “객체” 할당자 상태를 사용합니다. 그렇지 않으면 메인 인터프리터를 사용하는 상태를 공유합니다.이 값이
0이면check_multi_interp_extensions가1(0이 아님)이어야 합니다. 이 값이1이면gil이PyInterpreterConfig_OWN_GIL이면 안 됩니다.
-
int allow_fork¶
이 값이
0이면 서브 인터프리터가 활성화된 모든 스레드에서 프로세스 포크(fork)를 지원하지 않습니다. 그렇지 않으면 제한 없이 포크할 수 있습니다.포크가 허용되지 않는 경우에도
subprocess모듈은 여전히 작동합니다.
-
int allow_exec¶
이 값이
1이면 서브 인터프리터가 활성화된 모든 스레드에서 실행(exec, 예:os.execv())을 통한 현재 프로세스 교체를 지원하지 않습니다. 그렇지 않으면 제한 없이 실행할 수 있습니다.실행이 허용되지 않는 경우에도
subprocess모듈은 여전히 작동합니다.
-
int allow_daemon_threads¶
이 값이
0이면 서브 인터프리터의threading모듈에서 데몬 스레드를 생성하지 않습니다. 그렇지 않으면 데몬 스레드 생성이 허용됩니다(allow_threads가 0이 아닌 경우에 한함).
-
int check_multi_interp_extensions¶
이 값이
0이면 서브 인터프리터가 활성화된 모든 스레드에서 레거시(단일 단계 초기화) 모듈을 포함한 모든 확장 모듈을 가져올 수 있습니다. 그렇지 않으면 멀티 단계 초기화 확장 모듈(PEP 489 참조)만 가져올 수 있습니다. (Py_mod_multiple_interpreters도 참고하십시오.)use_main_obmalloc이0인 경우 이 값은 반드시1(0이 아님)이어야 합니다.
-
int gil¶
이 설정은 서브 인터프리터에 대한 GIL 동작을 결정합니다. 다음 중 하나일 수 있습니다:
-
PyInterpreterConfig_DEFAULT_GIL¶
기본 선택 사항(
PyInterpreterConfig_SHARED_GIL)을 사용합니다.
-
PyInterpreterConfig_SHARED_GIL¶
메인 인터프리터의 GIL을 사용(공유)합니다.
-
PyInterpreterConfig_OWN_GIL¶
서브 인터프리터 고유의 GIL을 사용합니다.
이 값이
PyInterpreterConfig_OWN_GIL인 경우,PyInterpreterConfig.use_main_obmalloc은 반드시0이어야 합니다.-
PyInterpreterConfig_DEFAULT_GIL¶
-
int use_main_obmalloc¶
-
PyStatus Py_NewInterpreterFromConfig(PyThreadState **tstate_p, const PyInterpreterConfig *config)¶
새로운 서브 인터프리터를 생성합니다. 이는 파이썬 코드 실행을 위한 (거의) 완전히 독립된 환경입니다. 특히, 새 인터프리터는
builtins,__main__,sys와 같은 기본 모듈을 포함하여 모든 가져온 모듈의 별개이고 독립적인 버전을 가집니다. 로드된 모듈 목록(sys.modules)과 모듈 검색 경로(sys.path)도 분리되어 있습니다. 새 환경에는sys.argv변수가 없습니다. 또한 새로운 표준 I/O 스트림 파일 객체인sys.stdin,sys.stdout및sys.stderr를 가집니다(단, 이들은 동일한 기반 파일 디스크립터를 참조합니다).제공된 config 는 인터프리터가 초기화될 때의 옵션을 제어합니다.
Upon success, tstate_p will be set to the first thread state created in the new sub-interpreter. This thread state is attached. Note that no actual thread is created; see the discussion of thread states below. If creation of the new interpreter is unsuccessful, tstate_p is set to
NULL; no exception is set since the exception state is stored in the attached thread state, which might not exist.Like all other Python/C API functions, an attached thread state must be present before calling this function, but it might be detached upon returning. On success, the returned thread state will be attached. If the sub-interpreter is created with its own GIL then the attached thread state of the calling interpreter will be detached. When the function returns, the new interpreter’s thread state will be attached to the current thread and the previous interpreter’s attached thread state will remain detached.
Added in version 3.12.
서브 인터프리터는 특정 기능이 제한되고 서로 격리되어 있을 때 가장 효과적입니다:
PyInterpreterConfig config = { .use_main_obmalloc = 0, .allow_fork = 0, .allow_exec = 0, .allow_threads = 1, .allow_daemon_threads = 0, .check_multi_interp_extensions = 1, .gil = PyInterpreterConfig_OWN_GIL, }; PyThreadState *tstate = NULL; PyStatus status = Py_NewInterpreterFromConfig(&tstate, &config); if (PyStatus_Exception(status)) { Py_ExitStatusException(status); }
설정(config)은 짧은 시간 동안만 사용되며 수정되지 않는다는 점에 유의하십시오. 초기화 과정에서 설정 값들은 다양한
PyInterpreterState값으로 변환됩니다. 읽기 전용 형태의 구성 복사본이PyInterpreterState내부에 저장될 수 있습니다.확장 모듈은 다음과 같이 (서브) 인터프리터 간에 공유됩니다.
예를 들어
PyModule_FromDefAndSpec()과 같이 다단계 초기화(multi-phase initialization)를 사용하는 모듈의 경우, 각 인터프리터마다 별도의 모듈 객체가 생성되고 초기화됩니다. 이러한 모듈 객체들 사이에는 C 수준의 정적 및 전역 변수만 공유됩니다.예를 들어
PyModule_Create()와 같은 레거시 단단계 초기화 를 사용하는 모듈의 경우, 특정 확장이 처음 임포트될 때 정상적으로 초기화되고 해당 모듈 딕셔너리의 (얕은) 사본이 별도로 저장됩니다. 다른 (서브) 인터프리터에서 동일한 확장을 임포트할 때 새로운 모듈이 초기화되고 이 복사본의 내용으로 채워지며, 이때 확장의init함수는 호출되지 않습니다. 결과적으로 모듈 딕셔너리에 있는 객체들이 (서브) 인터프리터 간에 공유되게 되어 원치 않는 동작을 유발할 수 있습니다(아래 Bugs and caveats 참조).이 상황은
Py_FinalizeEx()및Py_Initialize()를 호출하여 인터프리터를 완전히 재초기화한 후 확장을 임포트할 때 발생하는 경우와는 다릅니다. 해당 경우에는 확장의initmodule함수가 다시 호출됩니다. 다단계 초기화와 마찬가지로, 이 경우에도 모듈 간에 공유되는 것은 C 수준의 정적 및 전역 변수뿐입니다.
-
PyThreadState *Py_NewInterpreter(void)¶
- …의 일부 안정 ABI.
새로운 서브 인터프리터를 생성합니다. 이는 기존 동작을 유지하는 구성을 가진
Py_NewInterpreterFromConfig()를 래핑한 것입니다. 결과적으로 생성된 서브 인터프리터는 메인 인터프리터의 GIL을 공유하고, fork/exec 및 데몬 스레드를 허용하며, 단단계 초기화 모듈을 지원하는 비격리(unisolated) 상태가 됩니다.
-
void Py_EndInterpreter(PyThreadState *tstate)¶
- …의 일부 안정 ABI.
Destroy the (sub-)interpreter represented by the given thread state. The given thread state must be attached. When the call returns, there will be no attached thread state. All thread states associated with this interpreter are destroyed.
Py_FinalizeEx()will destroy all sub-interpreters that haven’t been explicitly destroyed at that point.
인터프리터별 GIL¶
Added in version 3.12.
Py_NewInterpreterFromConfig() 를 사용하면 고유한 GIL을 포함하여 다른 인터프리터와 완전히 격리된 서브 인터프리터를 생성할 수 있습니다. 이러한 격리의 가장 중요한 장점은 해당 인터프리터가 다른 인터프리터에 의해 차단되거나 다른 인터프리터스를 방해하지 않으면서 파이썬 코드를 실행할 수 있다는 점입니다. 따라서 단일 파이썬 프로세스에서 파이썬 코드를 실행할 때 여러 CPU 코어를 온전히 활용할 수 있습니다. 또한 이러한 격리는 단순히 스레드를 사용하는 것과는 다른 동시성 접근 방식을 권장합니다. (PEP 554 및 PEP 684 참조.)
고립된 인터프리터를 사용하려면 격리를 유지하는 데 주의가 필요합니다. 특히, 스레드 안전성에 대한 보장 없이 어떤 객체나 변경 가능한 상태도 공유하지 않는 것이 중요합니다. 일반적으로 불변하는 객체(예: None, (1, 5))라 할지라도 참조 카운트 때문에 공유할 수 없습니다. 이를 해결하는 간단하지만 덜 효율적인 방법 중 하나는 모든 상태(또는 객체) 사용에 글로벌 락을 두는 것입니다. 대안으로, 효과적으로 불변 객체(정수나 문자열 같은)는 참조 카운트에도 불구하고, 이들을 불멸적인 방식으로 만들어서 안전하게 처리할 수 있습니다. 실제로, 이 방식은 내장 싱글턴, 작은 정수, 그리고 다수의 다른 내장 객체에 대해 이미 수행되었습니다.
격리를 유지하면 프리 스레딩(free-threading)으로 인해 발생하는 복잡성 없이 적절한 멀티 코어 컴퓨팅을 수행할 수 있습니다. 격리를 유지하지 못하면 경쟁 상태나 디버깅하기 어려운 충돌을 포함하여 프리 스레딩의 모든 부작용에 노출될 수 있습니다.
그 외에도 여러 격리된 인터프리터를 사용할 때의 주요 과제 중 하나는 격리를 깨뜨리지 않으면서 효율적으로 서로 간에 통신하는 방법입니다. 현재 런타임과 표준 라이브러리는 이에 대한 표준 접근 방식을 제공하지 않습니다. 향후 표준 라이브러리 모듈이 추가되면 격리 유지에 드는 노력을 줄이고 인터프리터 간에 데이터를 소통(및 공유)하기 위한 효과적인 도구를 제공할 것입니다.
버그 및 주의 사항¶
서브 인터프리터(및 메인 인터프리터)는 동일한 프로세스의 일부이므로 그들 간의 격리가 완벽하지 않습니다. 예를 들어, os.close() 와 같은 저수준 파일 연산을 사용하면 (실수로 또는 의도적으로) 서로의 열려 있는 파일에 영향을 미칠 수 있습니다. (서브) 인터프리터 간에 확장이 공유되는 방식 때문에 일부 확장이 제대로 작동하지 않을 수 있으며, 이는 특히 단단계 초기화나 (정적) 전역 변수를 사용할 때 발생할 가능성이 높습니다. 한 서브 인터프리터에서 생성된 객체를 다른 (서브) 인터프리터의 네임스페이스에 삽입하는 것이 가능하므로, 가능한 경우 이를 피해야 합니다.
사용자 정의 함수, 메서드, 인스턴스 또는 클래스를 서브 인터프리터 간에 공유하지 않도록 각별히 주의해야 합니다. 이러한 객체에 의해 실행되는 임포트 작업이 잘못된 (서브) 인터프리터의 로드된 모듈 딕셔너리에 영향을 줄 수 있기 때문입니다. 또한 위의 항목들에 접근 가능한 객체를 공유하지 않는 것도 똑같이 중요합니다.
또한 이 기능과 PyGILState_* API를 함께 사용하는 것은 주의가 필요합니다. 이 API들은 파이썬 스레드 상태와 OS 수준의 스레드 사이에 일대일 대응 관계가 있다고 가정하는데, 서브 인터프리터의 존재로 인해 이 가정이 깨지기 때문입니다. 일치하는 한 쌍의 PyGILState_Ensure() 및 PyGILState_Release() 호출 사이에서 서브 인터프리터를 전환하지 않는 것을 강력히 권장합니다. 또한, 파이썬이 생성하지 않은 스레드에서 파이썬 코드를 호출하기 위해 이러한 API를 사용하는 확장(예: ctypes)은 서브 인터프리터를 사용할 때 제대로 작동하지 않을 가능성이 높습니다.
고수준 API¶
-
type PyInterpreterState¶
- …의 일부 안정 ABI (불투명 구조체로).
이 데이터 구조는 협력하는 여러 스레드에 의해 공유되는 상태를 나타냅니다. 동일한 인터프리터에 속한 스레드들은 모듈 관리 및 몇 가지 다른 내부 항목을 공유합니다. 이 구조체에는 공개 멤버가 없습니다.
서로 다른 인터프리터에 속한 스레드들은 가용 메모리, 열려 있는 파일 디스크립터 등과 같은 프로세스 상태를 제외하고는 초기 상태에서 아무것도 공유하지 않습니다. 전역 인터프리터 록(GIL) 또한 소속된 인터프리터와 관계없이 모든 스레드에 의해 공유됩니다.
버전 3.12에서 변경: PEP 684 는 인터프리터별 GIL 의 가능성을 도입했습니다.
Py_NewInterpreterFromConfig()를 참조하십시오.
-
PyInterpreterState *PyInterpreterState_Get(void)¶
- …의 일부 안정 ABI 버전 3.9 이후로.
현재 인터프리터를 가져옵니다.
Issue a fatal error if there is no attached thread state. It cannot return NULL.
Added in version 3.9.
-
int64_t PyInterpreterState_GetID(PyInterpreterState *interp)¶
- …의 일부 안정 ABI 버전 3.7 이후로.
인터프리터의 고유 ID를 반환합니다. 처리 과정에서 오류가 발생하면
-1을 반환하고 오류를 설정합니다.The caller must have an attached thread state.
Added in version 3.7.
-
PyObject *PyInterpreterState_GetDict(PyInterpreterState *interp)¶
- 반환값: 빌린 참조. …의 일부 안정 ABI 버전 3.8 이후로.
인터프리터별 데이터가 저장될 수 있는 딕셔너리를 반환합니다. 이 함수가
NULL을 반환하는 경우 예외가 발생하지 않은 것이며, 호출자는 인터프리터 전용 딕셔너리가 사용 가능하지 않다고 가정해야 합니다.이것은 확장 기능이 인터프리터별 상태 정보를 저장하기 위해 사용해야 하는
PyModule_GetState()를 대체하는 것이 아닙니다.반환된 딕셔너리는 인터프리터에서 빌려온 것이며, 인터프리터가 종료될 때까지 유효합니다.
Added in version 3.8.
-
typedef PyObject *(*_PyFrameEvalFunction)(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag)¶
프레임 평가 함수의 형식입니다.
throwflag 매개 변수는 제너레이터의
throw()메서드에서 사용됩니다. 0이 아니면 현재 예외를 처리합니다.버전 3.9에서 변경: 이제 이 함수는 tstate 매개 변수를 받습니다.
버전 3.11에서 변경: frame 매개 변수가
PyFrameObject*에서_PyInterpreterFrame*으로 변경되었습니다.
-
_PyFrameEvalFunction _PyInterpreterState_GetEvalFrameFunc(PyInterpreterState *interp)¶
프레임 평가 함수를 가져옵니다.
PEP 523 “CPython에 프레임 평가 API 추가”를 참조하십시오.
Added in version 3.9.
-
void _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState *interp, _PyFrameEvalFunction eval_frame)¶
프레임 평가 함수를 설정합니다.
PEP 523 “CPython에 프레임 평가 API 추가”를 참조하십시오.
Added in version 3.9.
-
void _PyInterpreterState_SetEvalFrameAllowSpecialization(PyInterpreterState *interp, int allow_specialization)¶
사용자 정의 프레임 평가자가 있는 경우 특수화(specialization) 기능을 활성화하거나 비활성화합니다.
allow_specialization 이 0이 아니면 사용자 정의 평가 프레임 함수가 설정되어 있더라도 적응형 특수화 도구(adaptive specializer)가 바이트코드를 계속해서 특성화합니다. allow_specialization 이 0인 경우, 사용자 정의 평가 프레임을 설정하면 특수화 기능이 비활성화됩니다. 표준 인터프리터 루프는 프레임 평가 API가 있는 동안에도 디옵트(deopt)를 수행하며, 프레임 평가 함수가 이를 활용하려면 특성화된 연산 코드를 처리해야 합니다.
Added in version 3.15.
-
int _PyInterpreterState_IsSpecializationEnabled(PyInterpreterState *interp)¶
인터프리터에 대한 적응형 특수화(adaptive specialization)가 활성화된 경우 0이 아닌 값을 반환합니다. 커스텀 평가 프레임 함수가 설정되지 않았거나, 또는 allow_specialization 이 활성화된 상태에서 하나가 설정된 경우 특수화가 활성화됩니다.
Added in version 3.15.
저수준 API¶
다음의 모든 함수는 반드시 Py_Initialize() 호출 후에 실행되어야 합니다.
버전 3.7에서 변경: Py_Initialize() now initializes the GIL
and sets an attached thread state.
-
PyInterpreterState *PyInterpreterState_New()¶
- …의 일부 안정 ABI.
Create a new interpreter state object. An attached thread state is not needed, but may optionally exist if it is necessary to serialize calls to this function.
인자 없이 감사 이벤트
cpython.PyInterpreterState_New를 발생시킵니다.
-
void PyInterpreterState_Clear(PyInterpreterState *interp)¶
- …의 일부 안정 ABI.
Reset all information in an interpreter state object. There must be an attached thread state for the interpreter.
인자 없이
cpython.PyInterpreterState_Clear를 수행하며 감사 이벤트 를 발생시킵니다.
-
void PyInterpreterState_Delete(PyInterpreterState *interp)¶
- …의 일부 안정 ABI.
Destroy an interpreter state object. There should not be an attached thread state for the target interpreter. The interpreter state must have been reset with a previous call to
PyInterpreterState_Clear().
고급 디버거 지원¶
이 함수들은 고급 디버깅 도구에서만 사용되도록 설계되었습니다.
-
PyInterpreterState *PyInterpreterState_Head()¶
해당 객체들의 리스트에서 머리에 위치한 인터프리터 상태 객체를 반환합니다.
-
PyInterpreterState *PyInterpreterState_Main()¶
메인 인터프리터 상태 객체를 반환합니다.
-
PyInterpreterState *PyInterpreterState_Next(PyInterpreterState *interp)¶
해당 객체들의 리스트에서 interp 다음에 위치한 인터프리터 상태 객체를 반환합니다.
-
PyThreadState *PyInterpreterState_ThreadHead(PyInterpreterState *interp)¶
interp 와 관련된 스레드 리스트에서 첫 번째
PyThreadState객체의 포인터를 반환합니다.
-
PyThreadState *PyThreadState_Next(PyThreadState *tstate)¶
동일한
PyInterpreterState객체에 속하는 모든 객체 리스트에서 tstate 다음의 스레드 상태 객체를 반환합니다.