2012-05-16 71 views
16

我在多線程C應用程序中嵌入python解釋器,我對使用哪些API來確保線程安全性有些困惑。在多線程C應用程序中嵌入python

從我所收集的內容來看,在嵌入python時,需要在調用任何其他Python C API調用之前由嵌入器負責照顧GIL鎖。這是用這些功能完成的:

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

但是,這似乎是不夠的。我仍然遇到隨機崩潰,因爲它似乎沒有爲Python API提供互斥。

閱讀一些文檔我還添加了後:

PyEval_InitThreads(); 
調用 Py_IsInitialized()之後

但是這也正是混淆的部分來了。該文檔指出此函數:

初始化和獲取全局解釋鎖

這表明,當這個函數返回時,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()'死鎖。 – khkarens

+0

這是嵌入Python和調用擴展模塊的唯一工具。 –

-1

讓多線程C應用程序嘗試從多個線程與單個CPython實例的多個Python線程進行通信對我來說看起來很危險。

只要一個C線程與Python通信,即使Python應用程序是多線程,也不必擔心鎖定。 如果你需要多個python線程,你可以用這種方式設置應用程序,並讓多個C線程通過一個隊列與這個單一的C線程進行通信,這些線程將它們分配到多個Python線程。

另一種可能適合你的方法是爲需要它的每個C線程分配一個CPython實例(當然Python程序之間的通信應該通過C程序)。

另一種替代方案可能是Stackless Python解釋器。這消除了GIL,但我不確定你遇到了將其綁定到多個線程的其他問題。 stackless是我的(單線程)C應用程序的替代品。

+1

你已經盡力而爲地不回答這個問題了。我不希望排隊工作到一個單一的線程。 – shoosh

5

我有完全相同的問題,現在通過在PyEval_InitThreads()之後立即使用PyEval_SaveThread()解決,正如您在上面建議的那樣。但是,我的實際問題是我在PyInitialise()之後使用了PyEval_InitThreads(),這導致PyGILState_Ensure()在從不同的後續本機線程調用時被阻塞。總之,這就是我現在做的事:

  1. 有全局變量:

    static int gil_init = 0; 
    
  2. 從主線程加載本地C擴展,並啓動Python解釋器:

    Py_Initialize() 
    
  3. 從多個其他線程我的應用程序同時進入Python/C API的很多調用:

    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() 
    

所有其他解決方案,我已經試過要麼導致隨機的Python sigfaults或死鎖/使用PyGILState_Ensure()阻塞。

Python文檔確實應該更加清晰,至少爲嵌入和擴展用例提供了一個例子。