Почему получение и получение GIL в двух потоках вызывает сбой приложения? - PullRequest
0 голосов
/ 14 января 2019

Я разработал расширение Python с использованием C ++. Единственная функция этого модуля примерно такая:

static PyObject *TestModule_API1(PyObject *self, PyObject *args)
{
   PyThreadState *_save;
   _save = PyEval_SaveThread();

   try
   {
      DoComputation1();

      PyEval_RestoreThread(_save);

   }
   catch (const std::exception & e)
   {    
      PyObject * py_exception = PyErr_NewException((char*)"pyhms.error", nullptr, nullptr);
      PyErr_SetString(py_exception, e.what());

      PyEval_RestoreThread(_save);

      return nullptr;
   }
   Py_RETURN_NONE;
}

Всякий раз, когда я вызываю этот метод с двумя потоками Python , если метод DoComputation1() выдает исключение, приложение вылетает. Даже помещение всего блока try в std :: mutex (блокировка в начале и разблокировка в конце блока) не решает проблему. В чем корень этой проблемы и как ее исправить?

Я занимаюсь разработкой для Windows 10 с использованием Visual Studio 2013 и Python 2.7.

Редактировать 1:
Если я перенесу строку PyEval_RestoreThread(_save); (в блоке catch) в начало блока catch, не произойдет сбоя. Означает ли это, что во время выпуска GIL я не должен вызывать какой-либо Python API?

Редактировать 2:
Мне также нужно защитить мой метод API1 от одновременных потоков с использованием мьютекса. Должен ли я заблокировать свой мьютекс перед выпуском GIL после этого? Есть ли случай, который может привести к тупику?

1 Ответ

0 голосов
/ 09 апреля 2019

В чем корень этой проблемы и как ее исправить?

Основной причиной проблемы является то, что если вы запускаете DoComputation1() в двух потоках и этот метод выдает исключение, оба потока будут запускать блок catch. В блоке catch были использованы некоторые функции Python API. Таким образом, это означает, что внутри реализации Python существуют два потока, которые приведут к сбою.

Если я принесу PyEval_RestoreThread (_save); строка (в блоке catch) к началу блока catch не происходит аварийное завершение. Значит ли это что во время выхода GIL я не должен Python API?

Если вы поместите строку PyEval_RestoreThread(_save); в первую строку блока catch, это означает, что код внутри блока catch будет выполняться двумя потоками последовательно. Так что сбоев не происходит.

Должен ли я заблокировать свой мьютекс до освобождения GIL после этого?

Я думаю, что лучше заблокировать их вместе, используя такую ​​конструкцию, как std::lock(...) или что-то в этом роде. Но для этого вам сначала понадобится класс-оболочка для GIL, чтобы сделать его блокируемым объектом.

Есть ли случаи, которые могут привести к тупику?

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

...