国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Python > 正文

不要用強制方法殺掉python線程

2019-11-25 16:20:33
字體:
來源:轉載
供稿:網友

前言:

    不要試圖用強制方法殺掉一個python線程,這從服務設計上就存在不合理性。 多線程本用來任務的協作并發,如果你使用強制手段干掉線程,那么很大幾率出現意想不到的bug。  請記住一點,鎖資源不會因為線程退出而釋放鎖資源 !

我們可以舉出兩個常見的例子:

1. 有個A線程拿到了鎖,因為他是被強制干掉的,沒能及時的release()釋放鎖資源,那么導致所有的線程獲取資源是都被阻塞下去,這就是典型的死鎖場景。

2.在常見的生產消費者的場景下,消費者從任務隊列獲取任務,但是被干掉后沒有把正在做的任務丟回隊列中,那么這就造成了數據丟失。

下面是java和python終止線程的方法:

java有三種方法可以使終止線程:

1. 使用退出標志,使線程正常退出,也就是當run方法完成后線程終止。
2. 使用stop方法強行終止線程(不推薦使用,因為stop和suspend、resume一樣,也可能發生不可預料的結果)。
3. 使用interrupt方法中斷線程。

python可以有兩種方法:

1. 退出標記
2. 使用ctypes強行殺掉線程

不管是python還是java環境下,理想的停止退出線程方法是 讓線程自個自殺,所謂的線程自殺就是 你給他一個標志位,他退出線程。

下面我們會采用多種方法來測試 停止python線程的異常情況。我們查看一個進程所有的執行線程,  進程是用過掌控資源,線程是用作調度單元,進程要被調度執行必須要有一個線程,默認的線程和進程的pid一樣的。

ps -mp 31449 -o THREAD,tid USER   %CPU PRI SCNT WCHAN USER SYSTEM  TIDroot   0.0  -  - -     -   -   -root   0.0 19  - poll_s  -   - 31449root   0.0 19  - poll_s  -   - 31450

獲取到了進程所有的線程后,通過strace得知 31450 是需要我們kill的線程id,當我們kill的時候,會出現整個進程都崩潰的情況。 在多線程環境下,產生的信號是傳遞給整個進程的,一般而言,所有線程都有機會收到這個信號,進程在收到信號的的線程上下文執行信號處理函數,具體是哪個線程執行的難以獲知。也就是說,信號會隨機發個該進程的一個線程。

strace -p <span style="font-size:14px;line-height:21px;">31450</span> Process <span style="font-size:14px;line-height:21px;">31450</span> attached - interrupt to quitselect(0, NULL, NULL, NULL, {0, 320326}) = 0 (Timeout)select(0, NULL, NULL, NULL, {1, 0})   = 0 (Timeout)select(0, NULL, NULL, NULL, {1, 0})   = 0 (Timeout)select(0, NULL, NULL, NULL, {1, 0})   = ? ERESTARTNOHAND (To be restarted)--- SIGTERM (Terminated) @ 0 (0) ---Process <span style="font-size:14px;line-height:21px;">31450</span> detached

上面出現的問題其實跟pthread的說明是一致的。當我們在python代碼里加入 signal 信號處理函數后,回調函數可以防止整個進程的退出,那么問題來了,通過信號函數不能識別你要干掉哪一個線程,也就是說,不能精準的干掉某個線程。你雖然把信號發給31450線程id,但是信號受理人是所屬進程的任何一個,另外傳給信號處理函數的參數只有信號數和信號stack而已,可有可無的。

加了信號處理后,不會退出進程

select(0, NULL, NULL, NULL, {1, 0})   = 0 (Timeout)select(0, NULL, NULL, NULL, {1, 0})   = ? ERESTARTNOHAND (To be restarted)--- SIGTERM (Terminated) @ 0 (0) ---rt_sigreturn(0xffffffff)        = -1 EINTR (Interrupted system call)select(0, NULL, NULL, NULL, {1, 0})   = 0 (Timeout)select(0, NULL, NULL, NULL, {1, 0})   = 0 (Timeout)

如果想從外部通知殺掉某個線程,那么可以構建使用rpc服務,或者別的方式通信,signal信號不可以,因為無法無法傳遞更多的信息。

python的線程不是模擬的,是真實的內核線程,內核調用pthread方法,但Python上層沒有提供關閉線程的方法,這就需要我們自己把握了。強烈推薦使用 event 或者 自定義標志位的方法, 如果非要強制殺掉線程,那么可以用python ctypes PyThreadState SetAsyncExc 方法強制退出,這樣對于運行的python服務沒有什么影響。

該函數的實現原理比較簡單,其實也是在python虛擬機里做個標示位,然后由虛擬機運行一個異常來取消線程,虛擬機會幫你做好try cache。 切記不要在外部殺掉python的某個線程,雖然你能通過ctypes找到線程id,但是你直接kill會干掉整個進程的。

下面的代碼是 用ctypes 殺掉線程的樣例,不推薦使用,因為太粗暴了.

import ctypes def terminate_thread(thread):  if not thread.isAlive():    return   exc = ctypes.py_object(SystemExit)  res = ctypes.pythonapi.PyThreadState_SetAsyncExc(    ctypes.c_long(thread.ident), exc)  if res == 0:    raise ValueError("nonexistent thread id")  elif res > 1:    ctypes.pythonapi.PyThreadState_SetAsyncExc(thread.ident, None)    raise SystemError("PyThreadState_SetAsyncExc failed")

咱們簡單look一下PyThreadState源代碼,總而言之觸發線程的異常模式。 有興趣的人可以閱讀 python pystate.c 的設計,配合著youtube的一些視頻分享。

 intPyThreadState_SetAsyncExc(long id, PyObject *exc) {  PyInterpreterState *interp = GET_INTERP_STATE();  ...  HEAD_LOCK();  for (p = interp->tstate_head; p != NULL; p = p->next) {    if (p->thread_id == id) {      從鏈表里找到線程的id,避免死鎖,我們需要釋放head_mutex。      PyObject *old_exc = p->async_exc;      Py_XINCREF(exc); #增加該對象的引用數      p->async_exc = exc; # 更為exc模式      HEAD_UNLOCK();      Py_XDECREF(old_exc); # 因為要取消,當然也就遞減引用      ...      return 1; #銷毀線程成功    }  }  HEAD_UNLOCK();  return 0;}

原生posix pthread 可以使用 ptread_cancel(tid) 在主線程中結束子線程。但是 Python 的線程庫不支持這樣做,理由是我們不應該強制地結束一個線程,這樣會帶來很多隱患,應該讓該線程自己結束自己。所以在 Python 中,推薦的方法是在子線程中循環判斷一個標志位,在主線程中改變該標志位,子線程讀到標志位改變,就結束自己。

類似這個邏輯:

def consumer_threading(): t1_stop= threading.Event() t1 = threading.Thread(target=thread1, args=(1, t1_stop))  t2_stop = threading.Event() t2 = threading.Thread(target=thread2, args=(2, t2_stop))  time.sleep(duration) #stop the thread2 t2_stop.set() def thread1(arg1, stop_event): while(not stop_event.is_set()):   #similar to time.sleep()   stop_event.wait(time)   pass  def thread2(arg1, stop_event): while(not stop_event.is_set()):   stop_event.wait(time)   pass

簡單的總結,雖然我們可以用ctypes里的pystats來控制線程,但這種粗暴中斷線程的方法是不合理的。 請選用 自殺模式 !如果你的線程正在發生io阻塞,而不能判斷事件怎么辦? 你的程序需要做優化了,最少在網絡io層需要有主動的timeout,避免一直的阻塞下去。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 宝鸡市| 来凤县| 观塘区| 遵义县| 城市| 无为县| 禹州市| 灌云县| 宜兰市| 屯昌县| 临江市| 阳泉市| 上蔡县| 汕头市| 旬邑县| 来宾市| 清水县| 旬邑县| 莱西市| 无棣县| 常熟市| 积石山| 安龙县| 大同市| 平阴县| 江北区| 房产| 镇江市| 泰和县| 正宁县| 澄迈县| 寿光市| 永吉县| 托里县| 中超| 中牟县| 华容县| 新闻| 洛宁县| 惠州市| 渝北区|