背景
開工前我就覺得有什麼不太對勁,感覺要背鍋。這可不,上班第三天就捅鍋了。
我們有個了不起的後台程式,可以動態載入模組,並以執行緒方式執行,通過這種形式實現外掛程式的功能。而模組更新時候,後台程式自身不會退出,只會將模組對應的執行緒關閉、更新**再啟動,6 得不行。
於是乎我就寫了個模組準備大展身手,結果忘記寫退出函式了,導致每次更新模組都新建立乙個執行緒,除非重啟那個程式,否則那些執行緒就一直苟活著。
這可不行啊,得想個辦法清理呀,要不然怕是要炸了。
那麼怎麼清理呢?我能想到的就是兩步走:
找出需要清理的執行緒號 tid;
銷毀它們;
找出執行緒id
和平時的故障排查相似,先通過 ps 命令看看目標程序的執行緒情況,因為已經是 setname 設定過執行緒名,所以正常來說應該是看到對應的執行緒的。 直接用下面**來模擬這個執行緒:
python 版本的多執行緒
輸出:
可以看到在 python 裡面輸出的執行緒名就是我們設定的那樣,然而 ps 的結果卻是令我懷疑人生:
正常來說不該是這樣呀,我有點迷了,難道我一直都是記錯了?用別的語言版本的多執行緒來測試下:
c 版本的多執行緒
輸出:
用 ps 命令再次驗證:
這個才是正確嘛,執行緒名確實是可以通過 ps 看出來的嘛!
不過為啥 python 那個看不到呢?既然是通過setname設定執行緒名的,那就看看定義咯:
看到這裡其實只是在thread物件的屬性設定了而已,並沒有動到根本,那肯定就是看不到咯~
這樣看起來,我們已經沒辦法通過ps或者/proc/這類手段在外部搜尋 python 執行緒名了,所以我們只能在 python 內部來解決。
於是問題就變成了,怎樣在 python 內部拿到所有正在執行的執行緒呢?
threading.enumerate可以完美解決這個問題!why?
because 在下面這個函式的 doc 裡面說得很清楚了,返回所有活躍的執行緒物件,不包括終止和未啟動的。
因為拿到的是 thread 的物件,所以我們通過這個能到該執行緒相關的資訊!
請看完整**示例:
輸出:
**看起來有點長,但是邏輯相當簡單,thread-test1和thread-test2都是列印出當前的 pid、執行緒 id 和 執行緒名字,然後 3s 後退出,這個是想模擬線程正常退出。
而checker執行緒則是每秒通過threading.enumerate輸出當前程序內所有活躍的執行緒。
可以明顯看到一開始是可以看到thread-test1和thread-test2的資訊,當它倆退出之後就只剩下mainthread和checker自身而已了。
銷毀指定執行緒
既然能拿到名字和執行緒 id,那我們也就能乾掉指定的執行緒了!
假設現在thread-test2已經黑化,發瘋了,我們需要制止它,那我們就可以通過這種方式解決了:
在上面的**基礎上,增加和補上下列**:
輸出一頓操作下來,雖然我們這樣對待thread-test2,但它還是關心著我們:多喝熱水,
ps: 熱水雖好,八杯足矣,請勿貪杯哦。
書回正傳,上述的方法是極為粗暴的,為什麼這麼說呢?
因為它的原理是:利用 python 內建的 api,觸發指定執行緒的異常,讓其可以自動退出;
萬不得已真不要用這種方法,有一定概率觸發不可描述的問題。切記!別問我為什麼會知道...
為什麼停止執行緒這麼難
多執行緒本身設計就是在程序下的協作併發,是排程的最小單元,執行緒間分食著程序的資源,所以會有許多鎖機制和狀態控制。
如果使用強制手段乾掉執行緒,那麼很大機率出現意想不到的bug。 而且最重要的鎖資源釋放可能也會出現意想不到問題。
我們甚至也無法通過訊號殺死程序那樣直接殺執行緒,因為 kill 只有對付程序才能達到我們的預期,而對付執行緒明顯不可以,不管殺哪個執行緒,整個程序都會退出!
而因為有 gil,使得很多童鞋都覺得 python 的執行緒是python 自行實現出來的,並非實際存在,python 應該可以直接銷毀吧?
然而事實上 python 的執行緒都是貨真價實的執行緒!
什麼意思呢?python 的執行緒是作業系統通過 pthread 建立的原生執行緒。python 只是通過 gil 來約束這些執行緒,來決定什麼時候開始排程,比方說執行了多少個指令就交出 gil,至於誰奪得花魁,得聽作業系統的。
如果是單純的執行緒,其實系統是有辦法終止的,比如:pthread_exit,pthread_kill或pthread_cancel
很可惜的是: python 層面並沒有這些方法的封裝!我的天,好氣!可能人家覺得,執行緒就該溫柔對待吧。
如何溫柔退出執行緒
想要溫柔退出執行緒,其實差不多就是一句廢話了~
要麼執行完退出,要麼設定標誌位,時常檢查標記位,該退出的就退出咯。
Android執行緒的建立與銷毀
一般會使用handler handler new handler 建立。這樣建立的handler是在主線程即ui執行緒下的handler,即這個handler是與ui執行緒下的預設looper繫結的。looper是用於實現訊息佇列和訊息迴圈機制的。因此,如果是預設建立handler那麼如果執行緒是做...
Python的執行緒之執行緒同步
目錄 在多執行緒程式中,它們互相獨立列印的時間卻是錯亂的!如下圖程式設計客棧,明明t 0 t 1 t 2 按照執行緒建立時間早晚排列 最後輸出居然是t 1最落後。我們怎麼樣做避免錯亂呢,下面看看。多執行緒,就是多個獨立的執行單位,同時執行同樣的事情。多執行緒不是已經做到同時執行了嗎?還需要同步幹嘛?...
Python多執行緒之event
事件 event 用於執行緒間同步和通訊。比如執行緒a要完成某一任務 event 執行緒b才能執行後面的 怎麼實現呢,就是用event。event常用方法 釋義set 開始乙個事件 wait 如果未設定set狀態會一直等待,否則過 clear 清除set狀態 isset 是否設定set狀態 注意 w...