Podsłuchy w języku Python nie są dobrze udokumentowane, a nawet dobrze obsługiwane. Poniższe wskazówki są najlepsze z moich niedoskonałości. Wydaje się, że działa dobrze w praktyce.
Threre to dwa ważne pojęcia, które należy zrozumieć, gdy zajmujemy się wątkami i podinterpretatorami w Pythonie. Po pierwsze interpreter Pythona nie jest tak naprawdę wielowątkowy. Ma globalny blok interpretera (GIL), który musi zostać zdobyty, aby wykonać prawie każdą operację w Pythonie (istnieje kilka rzadkich wyjątków od tej reguły).
Po drugie, każda kombinacja wątku i podinterpretera musi mieć własny stan wątku. Interpreter tworzy stan wątku dla każdego wątku zarządzanego przez niego, ale jeśli chcesz użyć Pythona z wątku nie utworzonego przez tego interpretera, musisz utworzyć nowy stan wątku.
Najpierw trzeba stworzyć sub tłumaczy:
zainicjować Python
Py_Initialize();
Initialize Pythona wsparcia wątek
Wymagane jeśli planują zadzwonić Python z wielu wątków). Wezwanie to również przejmuje GIL.
PyEval_InitThreads();
zapisać aktualny stan gwintu
mogę być używane PyEval_SaveThread()
, ale jego skutków ubocznych odłączający Gil, która następnie musi być wykupione.
PyThreadState* _main = PyThreadState_Get();
Tworzenie sub tłumaczy
PyThreadState* ts1 = Py_NewInterpreter();
PyThreadState* ts2 = Py_NewInterpreter();
przywracania stanu główny wątek interpreter
PyThreadState_Swap(_main);
Mamy teraz dwa stany wątku dla sub tłumaczy. Te stany gwintów są ważne tylko w wątku, w którym zostały utworzone. Każdy wątek, który chce użyć jednego z podinterpretatorów, musi utworzyć stan wątku dla tej kombinacji wątku i interpretera.
Używając podmenu interpreter z nowej nici
Oto przykład kodu za pomocą sub interpreter w nowej nici, który nie jest utworzony przez interpreter pomocniczym. Nowy wątek musi uzyskać GIL, utworzyć nowy stan wątku dla kombinacji wątku i interpretere i uczynić go bieżącym stanem wątku. Na koniec należy zrobić odwrotny proces oczyszczania.
void do_stuff_in_thread(PyInterpreterState* interp)
{
// acquire the GIL
PyEval_AcquireLock();
// create a new thread state for the the sub interpreter interp
PyThreadState* ts = PyThreadState_New(ts1->interp);
// make ts the current thread state
PyThreadState_Swap(ts);
// at this point:
// 1. You have the GIL
// 2. You have the right thread state - a new thread state (this thread was not created by python) in the context of interp
// PYTHON WORK HERE
// release ts
PyThreadState_Swap(NULL);
// clear and delete ts
PyThreadState_Clear(ts);
PyThreadState_Delete(ts);
// release the GIL
PyEval_ReleaseLock();
}
Teraz każdy wątek może wykonać następujące czynności:
thread1
do_stuff_in_thread(ts1->interp);
thread2
do_stuff_in_thread(ts1->interp);
Thread3
do_stuff_in_thread(ts2->interp);
Wywołanie Py_Finalize()
niszczy wszystkich subintererów. Alternatywnie można go zniszczyć ręcznie. Trzeba to zrobić w głównym wątku, używając stanów wątków utworzonych podczas tworzenia podprocesorów. Na koniec upewnij się, że główny wątek interpretera określa aktualny stan.
// make ts1 the current thread state
PyThreadState_Swap(ts1);
// destroy the interpreter
Py_EndInterpreter(ts1);
// make ts2 the current thread state
PyThreadState_Swap(ts2);
// destroy the interpreter
Py_EndInterpreter(ts2);
// restore the main interpreter thread state
PyThreadState_Swap(_main);
Mam nadzieję, że to trochę wyjaśni.
Mam mały kompletny przykład napisany w C++ na github.
Dzięki! Właśnie tego szukałem, "szczegółowego szybkiego startu" - bardziej szczegółowego niż szybkiego :). Gdyby nie ty, zdecydowanie przegapiłbym "stan nici dla tej kombinacji nici i tłumacza". – MariusSiuram
Podsumowując: NIE MOŻESZ korzystać z wielu interpreterów Pythona, które są naprawdę równoległe (wątki sprzętowe na wielu rdzeniach)? – japedo
Prawidłowo. Python używa globalnej blokady interpretera, która pozwala tylko pojedynczemu wątkowi na uruchamianie aktualnego kodu Pythona na raz. Jednak kod C, który wykonuje długą operację, często zwalnia blokadę, dopóki nie wróci do Pythona, aby można było wykonać inny wątek. Oznacza to, że rzeczywiste wykorzystanie będzie zależało od kodu. – sterin