Как вызвать многопоточный код Python из C ++?

Я пытаюсь подключиться к устаревшему многопотоковому приложению Python из разрабатываемого нового кода C ++. Кажется, я не могу заставить вызовы C ++ в Python работать надежно, если код Python запустил поток. В псевдокоде я пытаюсь заставить работать:

  • Бинарный файл C ++ запускается
  • Бинарный файл C ++ инициализирует среду Python и импортирует необходимые модули.
  • Python запускает один или несколько потоков
  • На каком-то этапе позже C ++ выполняет один или несколько вызовов функций Python.
  • ...
  • Программа на C ++ очищает среду Python и завершает работу.

Код, который у меня есть до сих пор, успешно вызывает функцию Python несколько раз из C ++, но дает сбой при выходе, а также единственный поток, созданный в Python, кажется, останавливается до тех пор, пока не будет завершена программа на C ++.

Текущие вызовы, которые я делаю для настройки среды Python:

  // Initialize Python environment
  Py_Initialize();

  // Initialize threading and acquire Python GIL
  PyEval_InitThreads();

  PyThreadState * mainThreadState = NULL;
  // save a pointer to the main PyThreadState object
  mainThreadState = PyThreadState_Get();
  // release the lock
  PyEval_ReleaseLock();

  // get the global lock
  PyEval_AcquireLock();
  // get a reference to the PyInterpreterState
  PyInterpreterState* mainInterpreterState = mainThreadState->interp;
  // create a thread state object for this thread
  PyThreadState* myThreadState = PyThreadState_New(mainInterpreterState);
  // free the lock
  PyEval_ReleaseLock();

  PySys_SetArgv(argc, argv);

Затем функция Python вызывается несколько раз следующим образом:

  // grab the global interpreter lock
  PyEval_AcquireLock();
  // swap in my thread state
  PyThreadState_Swap(myThreadState);

  PyObject* pMod = PyImport_ImportModule(moduleName.c_str());
  PyObject* pfn = PyObject_GetAttrString(pMod, fnName.c_str());

  // Create the data structure to pass the single C string
  PyObject* pargs = Py_BuildValue("(s)", arg.c_str());
  PyObject* result = PyEval_CallObject(pfn, pargs);

  Py_DECREF(pargs);
  Py_DECREF(pfn);
  Py_DECREF(pMod);

  // clear the thread state
  PyThreadState_Swap(NULL);
  // release our hold on the global interpreter
  PyEval_ReleaseLock();

Код однократной очистки, вызываемый, когда приложение C ++ готовится к завершению, имеет следующий вид:

  PyEval_AcquireLock();
  Py_Finalize();

Было бы здорово, если бы кто-нибудь мог указать мне на какой-то образец кода, который делает то, что я пытаюсь достичь, или мог бы указать, что я делаю неправильно в вышеуказанных шагах?


person fred basset    schedule 20.04.2017    source источник
comment
В итоге я заставил его работать, используя методы из первого ответа на этот другой пост SO: stackoverflow.com/questions/29595222/. Пример кода: gist.github.com/sterin/e8090d0451ab781a4e22   -  person fred basset    schedule 22.04.2017