Я использую Python/C API для вызова функций Python из C++. Моя проблема в том, что когда я вызываю функцию Python, которая, в свою очередь, импортирует scipy.optimize.least_squares, она зависает. Вот подробности...
Я вызываю свою функцию Python testfunc1(foo,bar=True) в модуле test_clib.py следующим образом:
PyObject* pTestModuleName = PyUnicode_FromString( "test_clib" );
PyObject* pTestModule = PyImport_Import( pTestModuleName );
PyObject* pFunction = PyObject_GetAttrString( pTestModule, "testfunc1" );
const char* str = "foo";
PyObject* pArgList = Py_BuildValue( "(s)", str );
PyObject* pKeywords = PyDict_New();
PyDict_SetItemString( pKeywords, "bar", Py_True );
PyObject* pReturn = PyObject_Call( pFunction, pArgList, pKeywords );
Когда testfunc1() импортирует scipy.optimize.least_squares, он будет зависать. Ему даже не нужно вызывать наименьшие_квадраты. Он будет висеть на этой строке:
from scipy.optimize import least_squares
Но когда я сведу это к простой тестовой программе, как я показал здесь, она работает. Когда это не удается, это когда приведенный выше фрагмент является частью моей более крупной программы.
Итак, я понимаю, что это не будет чем-то, что кто-то другой может попробовать напрямую, но, возможно, кто-то сможет заметить что-то, что мне не хватает.
Может быть, это будет полезно: когда я запускаю тестовую программу в gdb, она печатает, что запущено около дюжины потоков, но в моей простой тестовой программе нет ни одного потока, поэтому все они должны быть из C/Python API. Когда я пытаюсь запустить gdb в своей более крупной программе, которая выполняет те же вызовы Python, я не вижу запуска всех этих потоков, она просто зависает; когда я прерываю его, он прерывается здесь:
^C
Thread 18 "acamd" received signal SIGINT, Interrupt.0x00007ffff73ca7e8 in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
(gdb) where
#0 0x00007ffff73ca7e8 in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib6/libpthread.so.0
#1 0x00007ffff793bef5 in take_gil () from /lib64/libpython3.9.so.1.0
#2 0x00007ffff793c112 in PyEval_RestoreThread () from /lib64/libpython3.9.so.1.0
#3 0x00007ffff7a5dd8c in PyGILState_Ensure () from /lib64/libpython3.9.so.1.0
#4 0x00007fffb83bd6c5 in pybind11::detail::get_internals() () from /usr/local/lib64/python3.9/site-packages/scipy/spatial/_distance_pybind.cpython-39-x86_64-linux-gnu.so
#5 0x00007fffb83ae22c in PyInit__distance_pybind () from /usr/local/lib64/python3.9/site-packages/scipy/spatial/_distance_pybind.cpython-39-x86_64-linux-gnu.so
#6 0x00007ffff7a765bc in _imp_create_dynamic () from /lib64/libpython3.9.so.1.0
#7 0x00007ffff7989ac8 in cfunction_vectorcall_FASTCALL () from /lib6/libpython3.9.so.1.0
#8 0x00007ffff799a8eb in PyObject_Call () from /lib64/libpython3.9.so.1.0
#9 0x00007ffff7a089d8 in _PyEval_EvalFrameDefault () from /lib64/libpython3.9.so.1.0
#10 0x00007ffff79f084e in _PyEval_EvalCode () from /lib64/libpython3.9.so.1.0
#11 0x00007ffff 79f1e2b in _PyFunction_Vectorcall () from /lib64/libpython3.9.so.1.0
: : :
который продолжается еще около 200 строк.





трассировка (в частности, PyEval_RestoreThread) указывает, что поток застрял, пытаясь восстановить GIL (глобальную блокировку интерпретатора).
вещи, которые могут привести к этому моменту.
печать PyGILState_Check() = 1 непосредственно перед вызовом PyObject_Call( pFunction, pArgList, pKeywords ) в простой тестовой автономной версии и = 0 в полной версии, которая зависает. -- так ясно, что я делаю что-то другое, что я упускаю из виду!
@astronomerdave, может быть, вам просто нужно приобрести гил с помощью PyGILState_Ensure , а затем выпустить его? (см. связанный код), вы также можете проверить этот пример для потоковой обработки python с помощью c++, наконец, вам нужно проверить исключения python.
тот пример был именно таким. Добавление Py_BEGIN_ALLOW_THREADS / Py_END_ALLOW_THREADS в верхнюю/нижнюю часть my my main() и упомянутый вызов pyscope() внутри потока, который в конечном итоге вызывает функцию, содержащую экземпляр py (вызов Py_Initialize()), действительно было решением.
Спасибо @Ahmed, это большая вероятность. Я еще не нашел виновника, но это дает мне некоторое направление. Чего я пока не понимаю, так это того, что рабочий тестовый фрагмент представляет собой тот же код, связанный с Python, из нерабочей версии, поэтому я ничего не делаю по-другому (я не думаю!) — другими словами , если я надлежащим образом приобрел/выпустил GIL выше, то я должен сделать то же самое в нерабочем коде. Что отличается от неработающего кода, так это то, что показанный фрагмент вызывается из потока C++, а в рабочем тестовом коде он просто вызывается из main().