ANSWERED
Хорошо, я решил эту проблему. Все дело в том, как вы инициализируете состояние потока. Вам вообще не нужно использовать ReleaseLock. добавьте вызов InitThreads в определение вашего модуля:
BOOST_PYTHON_MODULE(ModuleName)
{
PyEval_InitThreads();
...
}
Хорошо, я пытался диагностировать эту проблему в течение нескольких часов и изучал то, что кажется, каждый пример в Интернете. Теперь устал, поэтому я могу упустить что-то очевидное, но вот что происходит :
Я оборачиваю библиотеку в boost python. Я запускаю сценарий python, который импортирует библиотеку, создает некоторые объекты, а затем получает обратные вызовы от c ++, который обращается обратно в python. Прежде чем я вызываю вызов любых функций python, я пытаюсь выполнить получить глобальную блокировку интерпретатора Вот пример кода:
class ScopedGILRelease
{
public:
inline ScopedGILRelease()
{
d_gstate = PyGILState_Ensure();
}
inline ~ScopedGILRelease()
{
PyGILState_Release(d_gstate);
}
private:
PyGILState_STATE d_gstate;
};
class PyTarget : public DingoClient::ClientRequest::Target, public wrapper<DingoClient::ClientRequest::Target>
{
public:
PyTarget(PyObject* self_) : self(self_) {}
~PyTarget() {
ScopedGILRelease gil_lock;
}
PyObject* self;
void onData(const boost::shared_ptr<Datum>::P & data, const void * closure)
{
ScopedGILRelease gil_lock;
// invoke call_method to python
}
...
}
Метод onData объекта Target вызывается библиотекой в качестве обратного вызова.В python мы наследуем от PyTarget и реализуем другой метод. Затем мы используем call_method <> для вызова этого метода. gil_lock получает блокировку и через RIAA гарантирует, что полученное состояние потока всегда является единственным выпуском и что оно фактически всегда освобождается при выходе из области видимости.
Однако, когда я запускаю это в сценарии, который пытается получить большое количество обратных вызовов для этой функции, всегда происходит сбой. Скрипт выглядит примерно так:
# Initialize the library and setup callbacks
...
# Wait until user breaks
while 1:
pass
Кроме того, скрипт python всегда создает объект, который запускается:
PyEval_InitThreads();
PyEval_ReleaseLock();
перед получением каких-либо обратных вызовов.
Я сократил код до такой степени, что я даже не вызываю python в onData, я просто получаю блокировку. При выпуске он всегда дает сбой либо:
Fatal Python error: ceval: tstate mix-up
Fatal Python error: This thread state must be current when releasing
, либо
Fatal Python error: ceval: orphan tstate
Fatal Python error: This thread state must be current when releasing
Это, по-видимому, случайное. Я сумасшедший, потому что мне кажется, что я правильно использую блокировку GIL, но, похоже, она вообще не работает.
Другие примечания: Только один поток когда-либо вызывает метод onData этого целевого объекта.
Когда я сплю в цикле while в вызывающем модуле python с time.sleep (), кажется, что скрипт может работать дольше, но в конечном итоге скрипт выйдет из строя с аналогичными проблемами. Количество времени, которое он длится, пропорционально количеству time.sleep (то есть time.sleep (10) работает дольше, чем time.sleep (0,01). Это заставляет меня задуматься о том, как скрипт повторно получает GIL без моего разрешения .
PyGILState_Release и PyGILState_Ensure вызываются нигде в моем коде, нигде еще не следует вызывать python.
Обновление
Я прочитал еще один вопрос, в котором предлагается импортировать потоки в модуль в качестве альтернативы выполнению
PyEval_InitThreads();
PyEval_ReleaseLock();
. Однако он не работает, когда я импортирую потоки перед своим модулем и удаляю две приведенные выше строки из моя оболочка для ускорения python.