По сути, существует массовая путаница/двусмысленность в отношении того, когда именно PyEval_InitThreads()
должен вызываться и какие сопутствующие вызовы API необходимы. официальная документация по Python, к сожалению, очень неоднозначна. Уже есть много вопросов по stackoverflow по этой теме, и действительно, Я лично уже задал вопрос, почти идентичный этому один, поэтому я не удивлюсь, если его закроют как дубликат; но учтите, что на этот вопрос, похоже, нет однозначного ответа. (К сожалению, у меня нет Гвидо Ван Россума на быстром наборе.)
Во-первых, давайте определим объем вопроса здесь: что я хочу сделать? Ну... Я хочу написать модуль расширения Python на C, который будет:
- Создавать рабочие потоки с помощью
pthread
API в C - Вызывать обратные вызовы Python из этих потоков C
Итак, давайте начнем с самой документации Python. В документах Python 3.2 сказано:
аннулировать PyEval_InitThreads()
Инициализировать и получить глобальную блокировку интерпретатора. Его следует вызывать в основном потоке перед созданием второго потока или выполнением любых других операций потока, таких как PyEval_ReleaseThread(tstate). Это не требуется перед вызовом PyEval_SaveThread() или PyEval_RestoreThread().
Итак, я понимаю, что вот что:
- Любой модуль расширения C, который порождает потоки, должен вызывать
PyEval_InitThreads()
из основного потока до того, как будут порождены любые другие потоки. - Вызов
PyEval_InitThreads
блокирует GIL
Итак, здравый смысл подсказывает нам, что любой модуль расширения C, создающий потоки, должен вызвать PyEval_InitThreads()
, а затем снять глобальную блокировку интерпретатора. Хорошо, кажется достаточно простым. Итак, на первый взгляд, все, что требуется, это следующий код:
PyEval_InitThreads(); /* initialize threading and acquire GIL */
PyEval_ReleaseLock(); /* Release GIL */
Кажется достаточно простым... но, к сожалению, в документации по Python 3.2 также говорится, что PyEval_ReleaseLock
был устарел. Вместо этого мы должны использовать PyEval_SaveThread
< /a> для выпуска GIL:
PyThreadState* PyEval_SaveThread()
Снимите глобальную блокировку интерпретатора (если она была создана и включена поддержка потоков) и сбросьте состояние потока в NULL, возвращая предыдущее состояние потока (которое не равно NULL). Если блокировка была создана, текущий поток должен ее получить.
Э... хорошо, я думаю, что модуль расширения C должен сказать:
PyEval_InitThreads();
PyThreadState* st = PyEval_SaveThread();
Действительно, это именно то, что этот ответ stackoverflow< /strong> говорит. За исключением тех случаев, когда я действительно пытаюсь сделать это на практике, интерпретатор 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()
просто необъяснимо seg-faults.
Хорошо... так что, возможно, по какой-то причине, которая в настоящее время находится за пределами моего понимания, модуль расширения C не нуждается в выпуске GIL. Я попробовал это... и, как и ожидалось, как только другой поток попытается получить GIL (используя PyGILState_Ensure), программа зависает из тупика. Так что да... вам действительно нужно освободить GIL после вызова PyEval_InitThreads()
.
Итак, снова вопрос: как вы выпускаете GIL после вызова PyEval_InitThreads()
?
И в более общем плане: что именно должен делать модуль расширения C, чтобы иметь возможность безопасно вызывать код Python из рабочих C-потоков?
c_extension
модуля здесь (его цель — вызвать ошибку вthreading
, будучи правильным для выявить ошибку в реализацииthreading
. Это не может вызвать ошибку на Python 3). - person jfs   schedule 18.03.2013