2012-05-16 3 views
16

파이썬 인터프리터를 멀티 스레드 C 응용 프로그램에 임베드하고 스레드 안전성을 보장하기 위해 사용해야하는 API에 대해 다소 혼란 스럽습니다.멀티 스레드 C 응용 프로그램에 파이썬 포함하기

파이썬을 임베드 할 때 다른 파이썬 C API 호출을 호출하기 전에 GIL 잠금을 처리하는 것이 임베드에 달려 있습니다. 이것은 다음과 같은 기능으로 수행됩니다 :

gstate = PyGILState_Ensure(); 
// do some python api calls, run python scripts 
PyGILState_Release(gstate); 

그러나 이것만으로는 충분하지 않습니다. 파이썬 API에 대한 상호 배제를 제공하지 않는 것 같기 때문에 여전히 임의의 충돌이 발생합니다. 바로 Py_IsInitialized() 호출 후

PyEval_InitThreads(); 

하지만 혼란 부분은 오는 곳이다 :

나는 또한 추가 좀 더 문서를 읽은 후.

초기화를 글로벌 인터프리터가

이이 함수가 반환이의 GIL가 가정 할 때 잠을 어떻게 든 잠금을 해제해야한다고 제안 잠금 획득 : 워드 프로세서는이 기능을 것을 주장. 실제로는 이것이 필요하지는 않습니다. 이 라인을 제 자리에 놓으면 멀티 스레드가 완벽하게 작동하고 상호 배제는 PyGILState_Ensure/Release 함수에 의해 유지됩니다.
PyEval_ReleaseLock()PyEval_ReleaseLock() 다음에 추가하려고 시도하면 PyImport_ExecCodeModule()에 대한 후속 호출에서 앱이 매우 빠르게 데드 록킹됩니다.

그래서 내가 무엇을 놓치고 있습니까?

답변

4

결국 나는 그것을 알아 낸 것입니다.

PyEval_InitThreads(); 

후 당신은 제대로 주 스레드에 대한 GIL을 해제하는 동안

PyEval_SaveThread(); 

를 호출해야합니다.

+0

이것은 잘못되어 잠재적으로 위험합니다 :'PyEval_SaveThread'는 항상'PyEval_RestoreThread'와 함께 있어야합니다. 다른 곳에서 설명 된 것처럼 (http://stackoverflow.com/a/15471525/1600898) 초기화 후에 잠금을 해제하려고하면 안됩니다. Python에 그대로 두어 정규 작업의 일부로 릴리스하십시오. – user4815162342

+0

파이썬에 대한 모든 호출을 _Block_ _Allow_ 블록에 넣으면 왜 위험한 지 알 수 없습니다. 반면,'PyEval_SaveThread();'를 호출하지 않으면 주 스레드가 파이썬에 대한 다른 스레드의 액세스를 차단합니다. 다른 말로하면'PyGILState_Ensure()'교착 상태입니다. – khkarens

+0

이것은 파이썬을 임베딩하고 확장 모듈을 호출하는 데 모두 작용하는 유일한 것입니다. –

-1

다중 스레드 C 응용 프로그램이 다중 스레드에서 단일 CPython 인스턴스의 여러 Python 스레드로 통신하는 것이 위험 해 보입니다.

오직 하나의 C 스레드가 파이썬과 통신하는 한 파이썬 응용 프로그램이 멀티 스레딩이더라도 잠금에 대해 걱정할 필요가 없습니다. 여러 개의 파이썬 스레드가 필요한 경우이 방법으로 응용 프로그램을 설정하고 여러 개의 C 스레드가 대기열을 통해 단일 Python 스레드에 배포하는 단일 C 스레드와 통신하도록 할 수 있습니다.

대신에 CPython 인스턴스를 필요로하는 각 C 스레드마다 하나씩 여러 개의 CPython 인스턴스를 가질 수 있습니다 (물론 Python 프로그램 간의 통신은 C 프로그램을 통해 이루어져야합니다).

또 다른 대안으로 Stackless Python 인터프리터를 사용할 수 있습니다. 그것은 GIL을 사용하지 않지만 여러 스레드에 바인딩하는 다른 문제가 있는지 확신하지 못합니다. stackless는 내 (단일 스레드) C 응용 프로그램을 대체했습니다.

+1

질문에 실제로 답변하지 않은 것이 최선이었습니다. 나는 하나의 스레드에 작업을 대기열에 관심이 아니에요. – shoosh

5

정확하게 똑같은 문제가 있었는데 위의 제안처럼 PyEval_InitThreads() 바로 뒤에 PyEval_SaveThread()을 사용하여 해결되었습니다.그러나 실제 문제는 PyInitialise() 이후에 PyEval_InitThreads()을 사용했기 때문에 다른 후속 네이티브 스레드에서 호출 할 때 PyGILState_Ensure()이 호출되는 것을 차단했습니다. 전역 변수가

  1. : 메인 스레드에서

    static int gil_init = 0; 
    
  2. 네이티브 C 확장을로드하고 파이썬 인터프리터 시작 :

    Py_Initialize() 
    
  3. 을 요약하면, 이것은 내가 지금 무엇을
  4. 다른 여러 스레드에서 내 응용 프로그램이 동시에 Python/C API에 많은 호출을합니다.

    메인 스레드에서
    if (!gil_init) { 
        gil_init = 1; 
        PyEval_InitThreads(); 
        PyEval_SaveThread(); 
    } 
    state = PyGILState_Ensure(); 
    // Call Python/C API functions...  
    PyGILState_Release(state); 
    
  5. 는 파이썬 인터프리터

    Py_Finalize() 
    

나도 해봤 다른 모든 솔루션을 중지 PyGILState_Ensure()를 사용하여 차단하는 임의의 파이썬 sigfaults 또는 교착 상태를/일으켰습니다.

파이썬 문서는 실제로 이것에 대해 더 분명해야하고 적어도 임베딩 및 확장 사용 사례에 대한 예제를 제공해야합니다.

관련 문제