Многопоточност с Python и C api

Имам C++ програма, която използва C api, за да използва моя Python библиотека. И библиотеката на Python, И кодът на C++ са многонишкови.

По-специално, една нишка от програмата на C++ инстанцира обект на Python, който наследява от threading.Thread. Имам нужда от всички мои C++ нишки, за да мога да извиквам методи на този обект.

Още от първите ми опити (наивно просто инстанцирам обекта от основната нишка, след това изчакам известно време, след което извиквам метода) забелязах, че изпълнението на нишката на Python, свързана с току-що създадения обект, спира веднага след като изпълнението се върне към програмата C++.

Ако изпълнението остане с Python (например, ако извикам PyRun_SimpleString("time.sleep(5)");), изпълнението на нишката на Python продължава във фонов режим и всичко работи добре, докато чакането приключи и изпълнението се върне към C++.

Явно правя нещо нередно. Какво трябва да направя, за да направя и моите C++ и Python многонишкови и способни да работят добре един с друг? Нямам предишен опит в областта, така че, моля, не предполагайте нищо!


person Matteo Monti    schedule 12.04.2015    source източник


Отговори (2)


Правилният ред от стъпки за изпълнение на това, което се опитвате да направите, е:

  • В основната тема:

    1. Initialize Python using Py_Initialize*.
    2. Инициализирайте поддръжката на нишки на Python с помощта на PyEval_InitThreads().
    3. Стартирайте C++ нишката.

В този момент основната нишка все още държи GIL.

  • In a C++ thread:
    1. Acquire the GIL using PyGILState_Ensure().
    2. Създайте нов обект на нишка на Python и го стартирайте.
    3. Освободете GIL с помощта на PyGILState_Release().
    4. Спи, направи нещо полезно или излез от темата.

Тъй като основната нишка съдържа GIL, тази нишка ще чака да придобие GIL. Ако основната нишка извиква API на Python, тя може да освобождава GIL от време на време, позволявайки нишката на Python да се изпълни за известно време.

  • Back in the main thread:
    1. Release the GIL, enabling threads to run using PyEval_SaveThread()
    2. Преди да се опитате да използвате други извиквания на Python, вземете отново GIL с помощта на PyEval_RestoreThread()

Подозирам, че пропускате последната стъпка - освобождаване на GIL в основната нишка, което позволява на нишката на Python да се изпълни.

Имам малък, но пълен пример, който прави точно това в тази връзка.

person sterin    schedule 19.04.2015

Вероятно не отключвате Global Interpreter Lock, когато извиквате обратно от python threading.Thread.

Е, ако използвате чист C API на python, имате тук има малко документация за това как да освободите/придобите GIL. Но докато използвам C++, трябва да ви предупредя, че той може да се повреди при всякакви изключения, въведени във вашия C++ код. Вижте тук.

По принцип всяка ваша C++ функция, която работи твърде дълго, трябва да отключи GIL и да се заключи, когато отново използва C Python API.

person Arpegius    schedule 12.04.2015
comment
Съжалявам, но изглежда не го разбирам правилно. Ето какво правя: 1) PyGILState_STATE gstate; gstate = PyGILState_Ensure(); 2) Създавам обект на Python, който наследява от threading.Thread 3) PyGILState_Release(gstate); 4) Заспивам за няколко секунди (в C++) Във функцията за изпълнение обектът трябва да отпечата нещо от време на време, но прави това очевидно само за първите няколко милисекунди. Какво правя грешно..? - person Matteo Monti; 13.04.2015