[問題] 停止正在執行 3rd party lib 的 thread

作者: carylorrk (carylorrk)   2014-08-22 10:03:15
程式執行登入時需要在另一個裝置做一些認證
在認證完成前原本程式的 UI 可以取消登入
但是此時做認證的 thread block 在一個 3rd party lib 等待認證回應的 function
所以不能用一般在 thread 下 check event 的方式來決定要不要離開
Google 了一下,找到利用 thread-id raise exception 的方式
http://stackoverflow.com/questions/323972/is-there-any-way-to-kill-a-thread-in-python
shorten:
http://goo.gl/MRRDFA
但是怎麼試也無法成功...都是 invalid thread ID
嘗試用文中的 walk 找 tid,或是用 threading.current_thread().ident
得到的都是一個很長的數字,也許不是真正的 tid?
所以試着用 system call 的方式
libc = ctypes.cdll.LoadLibrary('libc.so.6')
tid = libc.syscall(186)
的方式來得到 tid
但是丟進去 PyThreadState_SetAsyncExc 裏還是 invalid tid...
也就是 return 是 0
請問該如何得到正確的 tid?
或是有其他方式可以 kill thread? (最好是 portable 的)
也順便問問是否有其他更好的做法可以避免需要 kill thread?
作者: LiloHuang (十年一刻)   2014-08-22 20:45:00
我想先說明,終止線程跟 OS 本身有極高的相關性我印象中目前並無某種方法,或某個內建的 API可以保證的在所有的系統上,終止某個特定線程。至少 Win32 得用 TerminateThread,不同於 pthread我先假設你所使用的作業系統所操作的是 pthread無論是透過 get_ident 或用 ident 都是拿到 pthread_t也就是 pthread_self() 會拿到的內容 (可查源碼參照)你拿到的數值會有點大,我猜你的系統應該是 64 bitStackOverflow 那則被打勾的回應是常見的好方法至少這個方式不是真正強制硬砍掉 Thread 運行然而之所以你會拿到 invalid thread ID 的原因是你得改所有用到 PyThreadState_SetAsyncExc 的部分foobar = ctypes.pythonapi.PyThreadState_SetAsyncExcfoobar.argtypes = [ctypes.c_long, ctypes.py_object]res = foobar(tid, ctypes.py_object(exctype))請把原文章中的 _async_raise 前幾行,適度修改如上如此一來就應該不會出現 invalid thread ID 的狀況原因是在 64 bit 的環境中,得寫清楚帶進去的資料型態再透過 _async_raise(thr.ident, RuntimeError) 來終止至於是否有更好的做法? 可以開 child process 也是一種可以把傷害擺到另外一個進程,儘管還是會有機率出狀況最好的方式是改 3rd party lib 使其 graceful exit :D行有餘力還可以順便 merge 回 master/trunk 之類的 XD其實我也沒仔細看所有回應,只是要說 ctypes 在 64bit的環境得多設定 argtypes 或者直接類似該討論串中某篇直接生出 c_long(tid) 再進行帶入的動作...最好的方式就是用 event / mutex 實作 graceful exit或者用一個 atomic variable 來當作 stop flag 之類的畢竟沒看到 3rd party lib 的原始碼,我只能猜到這 XD至於 Python 還有個 GIL,如果用一些強制力砍掉 thread如果剛好被砍掉的 thread 是持有 GIL 的話,那就有趣了如果是 mission critical 的環境,就得考量更多的細節總之恭喜你有把問題解決囉 :D畢竟提及 child process 只是想避掉 GIL 這個老問題不應該有GIL的問題,誠如我一開始提到這不是強制砍掉它所謂的強制力是指 pthread_kill or TerminateThread然而如果能實作 graceful exit 還是最好的方向就是 :)因為 PyThreadState_SetAsyncExc 是請求觸發 exception並不會真的立刻把該 thread 給終止掉,聽起來雖然不錯然而如果該 thread 不幸卡在一個很久的 blocking I/O或者該 thread 剛好操作某個會卡很久的 C-API就不一定能如同原本預期,順利的把 thread 給終止掉了畢竟這個的原理是在 PyEval_EvalFrameEx 還沒跑 opcode之前進行檢查 tstate->async_exc 的值是否有被設定 :D可參考源碼瞭解更多細節http://goo.gl/878eMi 大概如此

Links booklink

Contact Us: admin [ a t ] ucptt.com