摘要:之前一直很好奇,都沒有屬性,為什么會有呢結(jié)果這玩意就是真的是僅作用于層手動笑臉結(jié)語區(qū)區(qū)一個可以引出很多本質(zhì)內(nèi)容的探索機會,比如線程的創(chuàng)建過程,管理流程等。
前言
使用 Python 都不會錯過線程這個知識,但是每次談到線程,大家都下意識說 GIL 全局鎖,
但其實除了這個老生常談的話題,還有很多有價值的東西可以探索的,譬如:setDaemon()。
線程的使用 與 存在的問題我們會寫這樣的代碼來啟動多線程:
import time import threading def test(): while True: print threading.currentThread() time.sleep(1) if __name__ == "__main__": t1 = threading.Thread(target=test) t2 = threading.Thread(target=test) t1.start() t2.start()
輸出:
^C^C^C^C^C^C^C # ctrl-c 多次都無法中斷 ^C ...(兩個線程競相打印)
通過 Threading 我們可以很簡單的實現(xiàn)并發(fā)的需求,但是同時也給我們帶來了一個大難題: 怎么退出呢?
在上面的程序運行中,我已經(jīng)嘗試按了多次的 ctrl-c,都無法中斷這程序工作的熱情!最后是迫不得已用 kill 才結(jié)束。
那么怎樣才能可以避免這種問題呢?或者說,怎樣才能在主線程退出的時候,子線程也自動退出呢?
守護線程有過相似經(jīng)驗的老司機肯定就知道,setDaemon() 將線程搞成 守護線程 不就得了唄:
import time import threading def test(): while True: print threading.currentThread() time.sleep(1) if __name__ == "__main__": t1 = threading.Thread(target=test) t1.setDaemon(True) t1.start() t2 = threading.Thread(target=test) t2.setDaemon(True) t2.start()
輸出:
python2.7 1.py(直接退出了)
直接退出?理所當(dāng)然,因為主線程已經(jīng)執(zhí)行完了,確實是已經(jīng)結(jié)束了,正因為設(shè)置了守護線程,所以這時候子線程也一并退出了。
突如其來的 daemon那么問題來了,我們以前學(xué) C 語言的時候,好像不用 Daemon 也可以啊,比如這個:
#include#include #include void *test(void *args) { while (1) { printf("ThreadID: %d ", syscall(SYS_gettid)); sleep(1); } } int main() { pthread_t t1 ; int ret = pthread_create(&t1, NULL, test, NULL); if (ret != 0) { printf("Thread create failed "); } // 避免直接退出 sleep(2); printf("Main run.. "); }
輸出:
# gcc -lpthread test_pytha.out & ./a ThreadID: 31233 ThreadID: 31233 Main run.. (毫不猶豫退出了)
既然 Python 也是用 C 寫的,為什么 Python 多線程退出需要 setDaemon ???
想要解決這個問題,我們怕不是要從主線程退出的一刻開始講起,從前....
反藤摸瓜Python 解析器在結(jié)束的時候,會調(diào)用 wait_for_thread_shutdown 來做個例行清理:
// python2.7/python/pythonrun.c static void wait_for_thread_shutdown(void) { #ifdef WITH_THREAD PyObject *result; PyThreadState *tstate = PyThreadState_GET(); PyObject *threading = PyMapping_GetItemString(tstate->interp->modules, "threading"); if (threading == NULL) { /* threading not imported */ PyErr_Clear(); return; } result = PyObject_CallMethod(threading, "_shutdown", ""); if (result == NULL) PyErr_WriteUnraisable(threading); else Py_DECREF(result); Py_DECREF(threading); #endif }
我們看到 #ifdef WITH_THREAD 就大概猜到對于是否多線程,這個函數(shù)是運行了不同的邏輯的
很明顯,我們上面的腳本,就是命中了這個線程邏輯,所以它會動態(tài) import threading 模塊,然后執(zhí)行 _shutdown 函數(shù)。
這個函數(shù)的內(nèi)容,我們可以從 threading 模塊看到:
# /usr/lib/python2.7/threading.py _shutdown = _MainThread()._exitfunc class _MainThread(Thread): def __init__(self): Thread.__init__(self, name="MainThread") self._Thread__started.set() self._set_ident() with _active_limbo_lock: _active[_get_ident()] = self def _set_daemon(self): return False def _exitfunc(self): self._Thread__stop() t = _pickSomeNonDaemonThread() if t: if __debug__: self._note("%s: waiting for other threads", self) while t: t.join() t = _pickSomeNonDaemonThread() if __debug__: self._note("%s: exiting", self) self._Thread__delete() def _pickSomeNonDaemonThread(): for t in enumerate(): if not t.daemon and t.is_alive(): return t return None
_shutdown 實際上也就是 _MainThread()._exitfunc 的內(nèi)容,主要是將 enumerate() 返回的所有結(jié)果,全部 join() 回收
而 enumerate() 是什么?
這個平時我們也會使用,就是當(dāng)前進程的所有 符合條件 的 Python線程對象:
>>> print threading.enumerate() [<_MainThread(MainThread, started 140691994822400)>]
# /usr/lib/python2.7/threading.py def enumerate(): """Return a list of all Thread objects currently alive. The list includes daemonic threads, dummy thread objects created by current_thread(), and the main thread. It excludes terminated threads and threads that have not yet been started. """ with _active_limbo_lock: return _active.values() + _limbo.values()
符合條件??? 符合什么條件?? 不著急,容我娓娓道來:
從起源談存活條件在 Python 的線程模型里面,雖然有 GIL 的干涉,但是線程卻是實實在在的原生線程
Python 只是多加一層封裝: t_bootstrap,然后再在這層封裝里面執(zhí)行真正的處理函數(shù)。
在 threading 模塊內(nèi),我們也能看到一個相似的:
# /usr/lib/python2.7/threading.py class Thread(_Verbose): def start(self): ...省略 with _active_limbo_lock: _limbo[self] = self # 重點 try: _start_new_thread(self.__bootstrap, ()) except Exception: with _active_limbo_lock: del _limbo[self] # 重點 raise self.__started.wait() def __bootstrap(self): try: self.__bootstrap_inner() except: if self.__daemonic and _sys is None: return raise def __bootstrap_inner(self): try: ...省略 with _active_limbo_lock: _active[self.__ident] = self # 重點 del _limbo[self] # 重點 ...省略
在上面的一連串代碼中,_limbo 和 _active 的變化都已經(jīng)標記了重點,我們可以得到下面的定義:
_limbo : 就是調(diào)用了 start,但是還沒來得及 _start_new_thread 的對象 _active: 活生生的線程對象
那么回到上文,當(dāng) _MainThread()._exitfunc 執(zhí)行時,是會檢查整個進程是否存在 _limbo + _active 的對象,
只要存在一個,就會調(diào)用 join(), 這個也就是堵塞的原因。
setDaemon 用處無限期堵塞不行,自作聰明幫用戶強殺線程也不是辦法,那么怎么做才會比較優(yōu)雅呢?
那就是提供一個途徑,讓用戶來設(shè)置隨進程退出的標記,那就是 setDaemon:
class Thread(): ...省略 def setDaemon(self, daemonic): self.daemon = daemonic ...省略 # 其實上面也貼了,這里再貼一次 def _pickSomeNonDaemonThread(): for t in enumerate(): if not t.daemon and t.is_alive(): return t return None
只要子線程,全部設(shè)置 setDaemon(True), 那么主線程一準備退出,全都乖乖地由操作系統(tǒng)銷毀回收。
之前一直很好奇,pthread 都沒有 daemon 屬性,為什么 Python 會有呢?
結(jié)果這玩意就是真的是僅作用于 Python 層(手動笑臉)
結(jié)語區(qū)區(qū)一個 setDaemon 可以引出很多本質(zhì)內(nèi)容的探索機會,比如線程的創(chuàng)建過程,管理流程等。
這些都是很有意思的內(nèi)容,我們應(yīng)該大膽探索,不局限于使用~
歡迎各位大神指點交流, QQ討論群: 258498217
轉(zhuǎn)載請注明來源: https://segmentfault.com/a/11...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/45134.html
摘要:中的多線程我參考了中的介紹,介紹的很入門很詳細。如只設(shè)置了第個和第個,沒有設(shè)置這只會掉第個子線程個人猜測,當(dāng)程序運行完主線程后則會檢查剩余的子線程,將最后面的且是子進程刪掉。第個沒有掉是因為線程還在運行并且是默認狀態(tài)不能被的。 本人初學(xué)者開始第一篇博客,記錄學(xué)習(xí)的點點滴滴,以作為備忘錄,也希望能同大家一起分享。有理解錯誤的地方希望大家指正。 python中的多線程我參考了(http:/...
摘要:而線程則是每秒通過輸出當(dāng)前進程內(nèi)所有活躍的線程。如果使用強制手段干掉線程,那么很大幾率出現(xiàn)意想不到的。只是通過來約束這些線程,來決定什么時候開始調(diào)度,比方說運行了多少個指令就交出,至于誰奪得花魁,得聽操作系統(tǒng)的。 背景 開工前我就覺得有什么不太對勁,感覺要背鍋。這可不,上班第三天就捅鍋了。 我們有個了不起的后臺程序,可以動態(tài)加載模塊,并以線程方式運行,通過這種形式實現(xiàn)插件的功能。而模塊...
摘要:從調(diào)用方法啟動線程,到方法執(zhí)行完畢或遇到未處理異常而中斷這段時間內(nèi),線程是激活的調(diào)用將會使主調(diào)線程堵塞,直到被調(diào)用線程運行結(jié)束或超時。對象實現(xiàn)了簡單的線程通信機制,它提供了設(shè)置信號,清除信號,等待等用于實現(xiàn)線程間的通信。 線程和進程 1、線程共享創(chuàng)建它的進程的地址空間,進程有自己的地址空間2、線程可以訪問進程所有的數(shù)據(jù),線程可以相互訪問 3、線程之間的數(shù)據(jù)是獨立的 4、子進程復(fù)制線程的...
閱讀 3490·2019-08-30 15:53
閱讀 3406·2019-08-29 16:54
閱讀 2190·2019-08-29 16:41
閱讀 2397·2019-08-23 16:10
閱讀 3377·2019-08-23 15:04
閱讀 1342·2019-08-23 13:58
閱讀 347·2019-08-23 11:40
閱讀 2452·2019-08-23 10:26