PyEval_InitThreads в Python 3: как / когда его вызывать? (сага продолжается до тошноты)

В основном там, кажется,массивный путаница / двусмысленность, когда именноPyEval_InitThreads() должен быть вызван, и какие сопутствующие вызовы API необходимы.официальная документация Python к сожалению очень неоднозначно. Уже естьмного вопросов по stackoverflow относительно этой темы, и действительно, ялично я ужезадал вопрос практически идентичный к этому, так что я выигралбыть особенно удивленным, если это закрыто как дубликат; но учтите, что, похоже, нет однозначного ответа на этот вопрос. (К сожалению, я неу Гвидо Ван Россума есть быстрый набор.)

Во-первых, давайтеОпределите суть вопроса здесь:что я хочу сделать? Хорошо ... Я хочу написать модуль расширения Python на C, который будет:

Spawn рабочие потоки с использованиемpthread API в CВызвать обратные вызовы Python из этих потоков C

Хорошо, так давайтеНачнем с самих документов Python.Python 3.2 документы сказать:

void PyEval_InitThreads ()

Инициализируйте и получите глобальную блокировку интерпретатора. Он должен вызываться в основном потоке перед созданием второго потока или участием в любых других операциях потока, таких как PyEval_ReleaseThread (tstate). Это не нужно перед вызовом PyEval_SaveThread () или PyEval_RestoreThread ().

Итак, мое понимание здесь таково:

Любой модуль расширения C, который порождает потоки, должен вызыватьPyEval_InitThreads() из основного потока, прежде чем будут созданы другие потокипризваниеPyEval_InitThreads запирает GIL

Так что здравый смысл говорит нам, что любой модуль расширения C, который создает потоки, должен вызыватьPyEval_InitThreads()и затем отпустите глобальную блокировку интерпретатора. Хорошо, кажется достаточно простым. Такprima facie, все это'Требуется следующий код:

PyEval_InitThreads(); /* initialize threading and acquire GIL */
PyEval_ReleaseLock(); /* Release GIL */

Кажется, достаточно просто ... но, к сожалению, документы по Python 3.2также скажи этоPyEval_ReleaseLock былосуждается, Вместо этого мыдолжен использоватьPyEval_SaveThread чтобы выпустить GIL:

PyThreadState * PyEval_SaveThread ()

Снимите глобальную блокировку интерпретатора (если она была создана и поддержка потоков включена) и сбросьте состояние потока в NULL, возвращая предыдущее состояние потока (которое не равно NULL). Если блокировка была создана, текущий поток должен ее получить.

Э-э ... хорошо, так что я думаю, что модуль расширения C должен сказать:

PyEval_InitThreads();
PyThreadState* st = PyEval_SaveThread();



Действительно, это именно то, чтоэтот ответ stackoverflow говорит. За исключением случаев, когда я на самом делепытаться на практике интерпретатор Python сразу же вызывает ошибки при импорте модуля расширения.Ницца.



Хорошо, теперь яЯ отказываюсь от официальной документации по Python и обращаюсь к Google. Так,этот случайный блог утверждает, что все, что вам нужно сделать из модуля расширения, это позвонитьPyEval_InitThreads(), Конечно, документация утверждает, чтоPyEval_InitThreads() приобретает GIL, и, действительно,быстрая проверка исходного кода дляPyEval_InitThreads() вceval.c показывает, что он действительно вызывает внутреннюю функциюtake_gil(PyThreadState_GET());

ТакPyEval_InitThreads() определенно приобретает GIL. Тогда я подумаю, что вам нужно как-то освободить GIL после вызова.PyEval_InitThreads()Но как? PyEval_ReleaseLock() устарела, иPyEval_SaveThread() просто необъяснимые ошибки.

Хорошо ... так что, может быть, по какой-то причине, которая в настоящее время за пределами моего понимания, модуль расширения Cне нужно выпустить GIL. Я попробовал это ... и, как и ожидалось, как только другой поток попытается получить GIL (используяPyGILState_Ensure), программа зависает из тупика. Так что да ... тыдействительно нужно освободить GIL после звонка.PyEval_InitThreads()

Итак, снова вопрос:как вы отпускаете GIL после звонка?PyEval_InitThreads()

И в целом:Что именно должен делать модуль C-extension, чтобы иметь возможность безопасно вызывать код Python из рабочих C-потоков?

Ответы на вопрос(7)

Ваш ответ на вопрос