2012-09-14 6 views
7

Jestem bardzo zdezorientowany, jak dokładnie mogę zapewnić bezpieczeństwo wątku podczas wywoływania kodu Pythona z wątku C (lub C++).Wywołanie kodu Pythona z wątku C

Python documentation zdaje się mówić, że zwykle idiom, aby to zrobić jest:

PyGILState_STATE gstate; 
gstate = PyGILState_Ensure(); 

/* Perform Python actions here. */ 
result = CallSomeFunction(); 
/* evaluate result or handle exception */ 

/* Release the thread. No Python API allowed beyond this point. */ 
PyGILState_Release(gstate); 

I rzeczywiście, ten stackoverflow answer zdaje się potwierdzać tyle. Ale komentator (z bardzo dobrą reputacją) mówi inaczej. Komentujący mówi, że powinieneś użyć PyEval_RestoreThread()/PyEval_SaveThread().

Docs wydaje się to potwierdzać:

PyThreadState* PyEval_SaveThread() 

Release the global interpreter lock (if it has been created and 
thread support is enabled) and reset the thread state to NULL, 
returning the previous thread state (which is not NULL). If the lock 
has been created, the current thread must have acquired it. (This 
function is available even when thread support is disabled at compile 
time.) 

void PyEval_RestoreThread(PyThreadState *tstate) 

Acquire the global interpreter lock (if it has been created and thread 
support is enabled) and set the thread state to tstate, which must not 
be NULL. If the lock has been created, the current thread must not have 
acquired it, otherwise deadlock ensues. (This function is available even 
when thread support is disabled at compile time.) 

Sposób docs opisać to, wydaje się, że PyEval_RestoreThread()/PyEval_SaveThread() jest w zasadzie idiom mutex lock/unlock. Więc byłoby sensowne, że przed wywołaniem jakiegokolwiek kodu Pythona z C, najpierw musisz zablokować GIL, a następnie odblokować go.

Więc co to jest? Podczas wywoływania kodu Pythona z C, należy użyć:

PyGILState_Ensure()/PyGILState_Release()

lub

PyEval_RestoreThread/PyEval_SaveThread?


A jaka jest naprawdę różnica?

Odpowiedz

6

Po pierwsze, prawie nigdy nie chcesz dzwonić pod numer PyEval_RestoreThread/PyEval_SaveThread. Zamiast tego chcesz wywołać makra opakowania Py_BEGIN_ALLOW_THREADS/Py_END_ALLOW_THREADS. Numer documentation jest zapisany dla tych makr, dlatego nie można go znaleźć.

W każdym razie, w żaden sposób, nie wykorzystujesz funkcji/makr wątku do nabycia GIL; używasz ich, aby tymczasowo uwolnić GIL, gdy go zdobędziesz.

Dlaczego więc miałbyś chcieć to zrobić? Cóż, w prostych przypadkach nie; potrzebujesz tylko Ensure/Release. Ale czasami musisz przytrzymać stan wątku Python do później, ale nie musisz trzymać GIL (a nawet wyraźnie nie musisz ani trzymać na GILu, aby umożliwić jakiś inny wątek, aby mógł się on uaktywnić, aby mógł zasygnalizować ci). Jak wyjaśniają doktorzy, najczęstszymi tego przyczynami są: wykonywanie operacji wejścia/wyjścia pliku lub rozległe obliczenia związane z procesorem.

Czy istnieje przypadek, w którym chcesz wywołać funkcje zamiast makr? Tak, jeśli chcesz uzyskać dostęp do ukrytego PyThreadState. Jeśli nie możesz wymyślić powodu, dla którego możesz tego chcieć, prawdopodobnie go nie masz.