python 多线程中的 join 和 daemon
文章目錄
- 第一關:簡單的 join()
- 第二關:join(timeout)
- 第三關:setDaemon(True)
第一關:簡單的 join()
import threading import timedef run():time.sleep(2)print('當前線程的名字是: ', threading.current_thread().name)time.sleep(2)if __name__ == '__main__':print('這是主線程:', threading.current_thread().name)thread_list = []for i in range(5):t = threading.Thread(target=run)thread_list.append(t)for t in thread_list:t.start()start_time = time.time()thread_list[0].join()print('一共用時:', time.time() - start_time)print('主線程結束!', threading.current_thread().name)執(zhí)行結果是:
這是主線程: MainThread 當前線程的名字是: Thread-1 當前線程的名字是: Thread-5當前線程的名字是: Thread-4當前線程的名字是: 當前線程的名字是: Thread-2 Thread-3 一共用時: 4.014104843139648 主線程結束! MainThread為了弄清楚 join() 的作用,我關注代碼 23 行的用時。
第 23 行,主線程阻塞,直到 thread_list[0] 退出后才往下執(zhí)行。
看打印結果第 7 行,用時 4s,這是好理解的,因為 thread_list[0] 大概用 4s。說明主線程確實阻塞了,一直阻塞到 thread_list[0] 執(zhí)行完畢。
我們把代碼修改一下,把 run() 函數的 sleep 時間增加。
def run():time.sleep(5)print('當前線程的名字是: ', threading.current_thread().name)time.sleep(5)預期結果是主線稱大約阻塞 10s
執(zhí)行結果是:
這是主線程: MainThread 當前線程的名字是: 當前線程的名字是: Thread-5當前線程的名字是: Thread-1當前線程的名字是: Thread-3 當前線程的名字是: Thread-2Thread-4一共用時: 10.009864091873169 主線程結束! MainThread和預期相符。
如果把 run 函數改成死循環(huán),比如
def run():while(True):time.sleep(2)print('當前線程的名字是: ', threading.current_thread().name)time.sleep(2)那么主線程就會一直阻塞,根本不會打印 “一共用時:…”,而是一直打印 “當前線程的名字是:…”
這也印證了主線程確實阻塞了,等待子線程退出,如果不退出就一直阻塞。
第二關:join(timeout)
在上面的例子中,如果子線程不返回,父線程就一直阻塞。如果需求是父線程阻塞一段時間,時間到了以后,就算子線程不返回,父線程也可以繼續(xù)向下執(zhí)行,那么我們可以給 join() 傳入一個時間參數。意思是最多等待這么多,等不到我就繼續(xù)往下執(zhí)行了。
實驗代碼是:
import threading import timedef run():time.sleep(2)print('當前線程的名字是: ', threading.current_thread().name)time.sleep(2)if __name__ == '__main__':print('這是主線程:', threading.current_thread().name)thread_list = []for i in range(5):t = threading.Thread(target=run)thread_list.append(t)for t in thread_list:t.start()start_time = time.time()thread_list[0].join(2)print('一共用時:', time.time() - start_time)print('主線程結束!', threading.current_thread().name)第 23 行:父線程最多等待 2s,等不到就繼續(xù)執(zhí)行
打印結果是:
這是主線程: MainThread 一共用時: 2.0001044273376465 主線程結束! MainThread 當前線程的名字是: 當前線程的名字是: 當前線程的名字是: Thread-2 Thread-3 Thread-1 當前線程的名字是: Thread-4 當前線程的名字是: Thread-5可以看到,父線程確實只阻塞了 2s。需要強調的是,主線程阻塞,不會對子線程造成任何影響,子線程依然執(zhí)行自己的代碼,根本不存在什么“主線程等待一段時間然后殺死子線程”,這是謬論。
當然,如果你傳入參數是 10,主線程不一定非要等 10s,如果子線程執(zhí)行了 3s 就返回了,那么主線程會立刻往下執(zhí)行,而不是繼續(xù)等待 7s。
例如把代碼第 23 行改成
thread_list[0].join(6)執(zhí)行結果是:
這是主線程: MainThread 當前線程的名字是: 當前線程的名字是: Thread-2 Thread-3當前線程的名字是: 當前線程的名字是: 當前線程的名字是: Thread-1Thread-4 Thread-5 一共用時: 4.028920888900757 主線程結束! MainThread第三關:setDaemon(True)
先上代碼:
import threading import timedef run():time.sleep(2)print('當前線程的名字是: ', threading.current_thread().name)time.sleep(2)if __name__ == '__main__':print('這是主線程:', threading.current_thread().name)thread_list = []for i in range(5):t = threading.Thread(target=run)t.setDaemon(True)thread_list.append(t)for t in thread_list:t.start()start_time = time.time()print('一共用時:', time.time() - start_time)print('主線程結束!', threading.current_thread().name)和之前不一樣的是第 17 行,多加了 t.setDaemon(True),這句話的意思是把線程的 daemon 屬性設成 True。daemon 有守護神的意思,也可以說把 t 設置為守護線程,守護誰呢?守護父線程嗎?咱們后面會分析。
網上充斥著這樣的解釋:如果一個子線程的 daemon 被設為 True,那么父線程結束了,這個子線程就立刻被干掉。是不是這樣呢?我們看執(zhí)行結果:
這是主線程: MainThread 一共用時: 0.0 主線程結束! MainThread似乎是這樣,主線程一執(zhí)行完,子線程就被殺死了,連打印都來不及。
我們把 15-18 行代碼改一下:
for i in range(5):t = threading.Thread(target=run)# t.setDaemon(True)thread_list.append(t)thread_list[0].setDaemon(True)也就是說,僅僅設置 thread_list[0] 的 daemon 為 True。預期結果是主線程一結束,thread_list[0] 就被殺死了,其余的子線程會繼續(xù)執(zhí)行。
結果是:
這是主線程: MainThread 一共用時: 0.0 主線程結束! MainThread 當前線程的名字是: Thread-3當前線程的名字是: Thread-2當前線程的名字是: 當前線程的名字是: Thread-5 當前線程的名字是: Thread-4Thread-1這就奇怪了,不是 Thread-1 被殺死了嗎,怎么后面還有打印呢?
肯定是我們的理解有問題。實際上,官方是這樣解釋 daemon 的:
A thread can be flagged as a “daemon thread”. The significance of this flag is that the entire Python program exits when only daemon threads are left.
以上摘自 https://docs.python.org/3/library/threading.html,黑體字是我加的。
也就是說,如果父線程執(zhí)行完畢,剩下的都是守護線程,那么這些守護線程也不用再執(zhí)行了,直接強制退出;如果父線程執(zhí)行完畢,剩下了 3 個守護線程和 1 個非守護線程,那么這 3 個守護線程還會繼續(xù)執(zhí)行,當非守護線程結束的時候,這些守護線程才會強制退出。
我們做個實驗:
import threading import timedef run(cnt):while cnt:time.sleep(1)print('當前線程的名字是: ', threading.current_thread().name)time.sleep(1)cnt = cnt - 1print(threading.current_thread().name, ' 退出')if __name__ == '__main__':print('這是主線程:', threading.current_thread().name)thread_list = []cnt_list = [2, 4, 4, 4, 4]for i in range(5):t = threading.Thread(target=run, args=(cnt_list[i],))t.setDaemon(True)thread_list.append(t)thread_list[0].setDaemon(False)for t in thread_list:t.start()start_time = time.time()print('一共用時:', time.time() - start_time)print('主線程結束!', threading.current_thread().name)5-11:每個子線程執(zhí)行時間不一樣,根據傳參 cnt 定
第 16 行,增加了cnt_list = [2, 4, 4, 4, 4],意圖是讓 thread_list[0] 執(zhí)行時間短一些,僅執(zhí)行 while 循環(huán) 2 次,其他子線程執(zhí)行 4 次
18-23:先把所有子線程設為守護線程,后面再把 thread_list[0] 設為非守護線程(偷懶了)
輸出結果:
這是主線程: MainThread 一共用時: 0.0 主線程結束! MainThread 當前線程的名字是: 當前線程的名字是: 當前線程的名字是: Thread-5 當前線程的名字是: Thread-2 Thread-1 Thread-4 當前線程的名字是: Thread-3 當前線程的名字是: Thread-1當前線程的名字是: 當前線程的名字是: 當前線程的名字是: Thread-3 當前線程的名字是: Thread-5Thread-4 Thread-2Thread-1 退出可以發(fā)現,父線程退出后,守護線程并沒有退出,當非守護線程 Thread-1 退出后,其他守護線程強制退出。
由此可見,守護線程不僅僅守護父線程,也會守護其他非守護線程。
以上就是本文的全部內容。歡迎各位老師批評斧正。
總結
以上是生活随笔為你收集整理的python 多线程中的 join 和 daemon的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 页面放在哪_思维制胜!PPT内容巨多的页
- 下一篇: 用python实现AES加密解密