2014-06-19 77 views
4

我將python嵌入到C++插件中。插件在每個會話中調用python算法幾十次,每次發送算法不同的數據。到目前爲止這麼好停止嵌入式python

但現在我有一個問題: 該算法有時需要幾分鐘才能解決並返回一個解決方案,並在那段時間經常條件改變使該解決方案不相關。所以,我想要的是在任何時候停止算法的運行,並在其他數據集之後立即運行它。

下面是嵌入蟒蛇的C++代碼,我到目前爲止有:

void py_embed (void*data){ 


counter_thread=false; 

PyObject *pName, *pModule, *pDict, *pFunc; 

//To inform the interpreter about paths to Python run-time libraries 
Py_SetProgramName(arg->argv[0]); 

if(!gil_init){ 
    gil_init=1; 
    PyEval_InitThreads(); 
    PyEval_SaveThread(); 
} 
PyGILState_STATE gstate = PyGILState_Ensure(); 

// Build the name object 
pName = PyString_FromString(arg->argv[1]); 
if(!pName){ 
    textfile3<<"Can't build the object "<<endl; 
} 

// Load the module object 
pModule = PyImport_Import(pName); 
if(!pModule){ 
    textfile3<<"Can't import the module "<<endl; 
} 

// pDict is a borrowed reference 
pDict = PyModule_GetDict(pModule); 
if(!pDict){ 
    textfile3<<"Can't get the dict"<<endl; 
} 

// pFunc is also a borrowed reference 
pFunc = PyDict_GetItemString(pDict, arg->argv[2]); 
if(!pFunc || !PyCallable_Check(pFunc)){ 
    textfile3<<"Can't get the function"<<endl; 
} 

/*Call the algorithm and treat the data that is returned from it 
... 
... 
*/ 

// Clean up 
Py_XDECREF(pArgs2); 
Py_XDECREF(pValue2); 
Py_DECREF(pModule); 
Py_DECREF(pName); 

PyGILState_Release(gstate); 

counter_thread=true; 
_endthread(); 

};

編輯:Python的算法是不是我的工作,我不應該改變它

+0

可以在算法被分解成,在運行小的步驟(理想有限的時間?)你的C++代碼可能是:'while(stillNeeded)performNextStep();' –

+0

不,算法不是我的工作,我不應該改變它 –

回答

4

很抱歉,但你的選擇是短。你可以改變python代碼(ok,插件 - 不是一個選項),或者在另一個PROCESS上運行它(其間有一些漂亮的ipc)。然後你可以使用系統API來擦除它。

+0

所以,如果我理解正確,你的建議是使用進程而不是線程?或者更好,把'py_embed'線程放到一個進程中並在我想要的時候殺掉它? 令人驚訝的是,在Python/C API中沒有任何選項來終止主線程或其他線程中的工作線程...... –

+3

@JoãoPereira在訪問某些未知內存部分時終止線程確實阻止它運行,但它會讓你的應用程序難以預測狀態。你可能會泄漏文件句柄或內存,破壞堆(假設你正在將內存返回堆中,並且根據某些指令,你只是停止......)或其他類似的操作。操作系統在終止(一個進程)時提供具有已知屬性的膠囊,所有附加的性能處罰都包含在這種包裝中。 – Yakk

+1

@JoãoPereira,那(控制)應該在線程內運行的Python代碼的協議(和參與 - 例如註冊一個信號)完成 - 但是如果你不能在Python方面得到手,你必須使用技巧來處理它。 – user430051

5

這是基於粗略的python知識,並快速閱讀python文檔。

PyThreadState_SetAsyncExc允許您向正在運行的python線程注入異常。

在某個線程中運行你的python解釋器。在另一個線程中,PyGILState_STATE然後PyThreadState_SetAsyncExc進入主線程。 (這可能需要一些前驅工作來教導python解釋器瞭解第二個線程)。

除非您正在運行的Python代碼充滿了「catch alls」,否則這應該會導致它終止執行。

您還可以查看代碼來創建python子解釋器,這可以讓您在舊關閉時啓動新腳本。

Py_AddPendingCall也很吸引人,但是周圍可能沒有足夠的警告。

+1

大多數「catch-alls」只捕獲子類Exception,所以你可以在另一個基類(即SystemExit)中使用異常來避免它們:https://docs.python.org/2/library/exceptions.html –

2

所以,我終於想到了一個解決方案(更多的是解決方法)。

不是終止正在運行算法的線程 - 我們稱之爲T1 - ,而是創建另一個-T2 - 與當時相關的一組數據。

在每個線程我這樣做:

thread_counter+=1; //global variable 
int thisthread=thread_counter; 

,並從蟒蛇的解決方案後,給我確認這是最「近」,一個從T1或T2:

if(thisthread==thread_counter){ 
    /*save the solution and treat it */ 
} 

計算機方面的努力顯然不是最好的解決方案,但它符合我的目的。

謝謝你的幫助球員

+3

你真的確定這個解決方案?你可能最終會遇到越來越多的線程,並且由於每個線程的cpu時間更少,你可能需要等待更長的時間。 –

+0

事實上,在經過一段時間的嘗試這個解決方案後,我發現問題正是你說的......時間去嘗試一些建議的解決方案 –

2

我一直在思考這個問題,我同意該子解釋可爲您提供一個可能的解決方案https://docs.python.org/2/c-api/init.html#sub-interpreter-support。它支持創建新口譯員並結束現有口譯員的呼叫。錯誤&注意事項部分描述了一些問題,這取決於您的架構可能會或可能不會造成問題。

另一種可能的解決方案是使用Python multiprocessing模塊,和你的工作線程測試中的一個全局變量(類似time_to_die)。然後從父母那裏,你抓住GIL,設置變量,釋放GIL並等待孩子完成。

但後來又有一個想法發生在我身上。爲什麼不使用fork(),在子進程中初始化你的python解釋器,當父進程決定是python線程結束的時候,就殺了它。事情是這樣的:

void process() { 

    int pid = fork(); 
    if (pid) { 
     // in parent 
     sleep(60); 
     kill(pid, 9); 
     } 
    else{ 
     // in child 
     Py_Initialize(); 
     PyRun_SimpleString("# insert long running python calculation"); 
     } 
    } 

(這個例子假設* nix中,如果你使用的是Windows,替補的CreateProcess()/了TerminateProcess())

+0

建議1不好,因爲我認爲任何新的子解釋器都必須導入模塊再次,這需要一些時間.. 至於其他2個建議,我更喜歡fork()更好(即使我不知道它是什麼),但我肯定會考慮兩個。 到目前爲止的最佳答案,我爲此獎勵獎金 –

+0

只是一件事,我在Windows下,並不能很容易地理解這個CreateProcess()。你可以更新你的例子從Linux到Windows? –

+0

CreateProcess是用於分叉子進程的win32調用,與父進程斷開連接。這裏有一個簡單的例子http://msdn.microsoft.com/en-us/library/windows/desktop/ms682512%28v=vs.85%29.aspx。這個概念和fork()是一樣的,儘管微軟的調用有很多額外的選項(其中大部分可以是NULL)。你的新孩子的第一個動作是初始化python和「做它的事情」。然後你可以隨時殺死它 - 完全安全。當你選擇殺死父母時,它不會對父母造成影響。 – user590028