2012-05-16 2 views
16

Я внедряю интерпретатор python в многопоточное приложение C, и я немного запутался в отношении того, какие API-интерфейсы я должен использовать для обеспечения безопасности потоков.Встраивание python в многопоточное приложение C

Из того, что я собрал, при встраивании python перед встраиванием нужно позаботиться о блокировке GIL перед вызовом любого другого вызова API Python C. Это делается с помощью этих функций:

gstate = PyGILState_Ensure(); 
// do some python api calls, run python scripts 
PyGILState_Release(gstate); 

Но этого, по-видимому, недостаточно. У меня все еще были случайные сбои, так как он, похоже, не предусматривает взаимного исключения для API-интерфейсов Python.

После прочтения еще нескольких докторов я также добавил:

PyEval_InitThreads(); 

сразу после вызова Py_IsInitialized(), но вот где запутанная часть приходит. Документы утверждают, что эта функция:

Initialize и приобретают глобальный интерпретатор запирать

Это говорит о том, что, когда эта функции возвращает, то GIL должен быть заблокировано и должно быть разблокировано как-то. но на практике это, как представляется, не требуется. Благодаря этой линии моя многопоточная работа отлично работала, и взаимное исключение поддерживалось функциями PyGILState_Ensure/Release.
Когда я попытался добавить PyEval_ReleaseLock() после PyEval_ReleaseLock() приложение с закрытыми окнами довольно быстро в последующем вызове PyImport_ExecCodeModule().

Так что же мне здесь не хватает?

ответ

4

В конце концов я понял.
После

PyEval_InitThreads(); 

Вам нужно позвонить

PyEval_SaveThread(); 

В то время как правильно освободить GIL для основного потока.

+0

Это неправильно и потенциально опасно: 'PyEval_SaveThread' всегда должен быть соединен с' PyEval_RestoreThread'. Как [объяснено в другом месте] (http://stackoverflow.com/a/15471525/1600898), вы не должны пытаться освободить блокировку после ее инициализации; просто оставьте его на Python, чтобы выпустить его как часть своей обычной работы. – user4815162342

+0

Я не понимаю, почему это вредно, если вы положили все вызовы на python в блоки _Block_ _Allow_. С другой стороны, если вы не вызываете 'PyEval_SaveThread();', то ваш основной поток блокирует доступ других потоков к Python. Другими словами, 'PyGILState_Ensure()' deadlocks. – khkarens

+0

Это единственное, что работает как для встраивания Python, так и для вызова в модуль расширения. –

-1

Наличие многопоточного приложения C, пытающегося связываться из нескольких потоков с несколькими потоками Python одного экземпляра CPython, выглядит для меня рискованным.

Пока только один поток C взаимодействует с Python, вам не нужно беспокоиться о блокировке, даже если приложение Python является многопоточным. Если вам нужно несколько потоков python, вы можете настроить приложение таким образом и иметь несколько потоков C, связывающихся через очередь с одним единственным потоком C, который обрабатывает их до нескольких потоков Python.

Альтернативой, которая может сработать для вас, является наличие нескольких экземпляров CPython для каждого потока C, который ему нужен (конечно, связь между программами Python должна осуществляться через программу C).

Другой альтернативой может быть интерпретатор Stackless Python. Это устраняет проблему с GIL, но я не уверен, что вы столкнулись с другими проблемами, связанными с несколькими потоками. stackless была заменой для моего (однопоточного) приложения C.

+1

Вы сделали все возможное, не отвечая на вопрос. Меня не интересует очередь на работу в один поток. – shoosh

5

У меня была точно такая же проблема, и теперь она решена с помощью PyEval_SaveThread() сразу после PyEval_InitThreads(), как вы предлагаете выше.Однако моя фактическая проблема заключалась в том, что я использовал PyEval_InitThreads() после PyInitialise(), который затем вызвал PyGILState_Ensure() для блокировки при вызове из разных последующих последовательных потоков. Короче говоря, это то, что я делаю сейчас:

  1. Существует глобальная переменная:

    static int gil_init = 0; 
    
  2. Из основного потока загрузки нативный расширение C и запустить интерпретатор Python:

    Py_Initialize() 
    
  3. Из нескольких других потоков мое приложение одновременно делает много вызовов в API Python/C:

    if (!gil_init) { 
        gil_init = 1; 
        PyEval_InitThreads(); 
        PyEval_SaveThread(); 
    } 
    state = PyGILState_Ensure(); 
    // Call Python/C API functions...  
    PyGILState_Release(state); 
    
  4. Из главного потока остановить переводчику Python

    Py_Finalize() 
    

Все другие решения, которые я пробовал вызваны либо случайные sigfaults Python или затор/блокировка с помощью PyGILState_Ensure().

Документация на Python действительно должна быть более понятной и, по крайней мере, служить примером как для случаев внедрения, так и для расширения.