本文鏈接:https://www.cnblogs.com/tujia/p/13684251.html
背景:來觀察測試一下python 線程(Thread)的阻塞、普通線程和守護線程有什么區別、線程池又是什么
一、公共代碼
首先先貼上一些公共代碼,下面的例子都基於這份公共代碼運行(注:替換xxx的內容)
import time import threading from concurrent.futures import ThreadPoolExecutor def worker(name): print('%s start...' % name) time.sleep(2) print('%s done.' % name) def xxx(): pass if __name__ == '__main__': xxx()
二、單線程阻塞
def 單線程阻塞(): t = threading.Thread(target=worker, args=('張三',)) t.start() # 阻塞 t.join() print('Finished')
運行結果:
解釋:阻塞線程的情況下,程序會先等待線程任務執行完,再往下執行其他代碼
三、單線程不阻塞
def 單線程不阻塞(): t = threading.Thread(target=worker, args=('李四',)) t.start() print('Finished')
運行結果:
解釋:不阻塞線程的情況下,程序會直接往下走,線程任務是后完成的(因為我在線程任務里加了 sleep),類似於異步;同時,我們還可以發現,程序(主線程)執行完最后一行代碼之后,如果線程任務還沒完成,程序是不會馬上死掉的,還是會等線程任務執行完才會結束程序。
四、多線程的錯誤阻塞
def 多線程的錯誤阻塞(): t1 = threading.Thread(target=worker, args=('張三',)) t1.start() t1.join() t2 = threading.Thread(target=worker, args=('李四',)) t2.start() t2.join() print('Finished')
運行結果:
解釋:t1.join 直接阻塞了程序,t2還沒start,t1.join阻塞程序直到t1的任務已完成。所以會看到 張三 done 之后,李四 才能 start
五、多線程的正確阻塞
def 多線程的正確阻塞(): t1 = threading.Thread(target=worker, args=('張三',)) t2 = threading.Thread(target=worker, args=('李四',)) t1.start() t2.start() t1.join() t2.join() print('Finished')
運行結果:
解釋:需要將所以子線程都start之后,才能阻塞(join);線程任務也不是按順序完成了,哪個先完成,得看哪個線程任務耗時少,也有可能會同時完成(並發)
按下面代碼,修改一下worker函數,給李四加一下速,再執行一次看看:
def worker(name): print('%s start...' % name) time.sleep(2 if name == '張三' else 1) print('%s done.' % name)
注:這次是李四先done了~
溫馨提示:測試完記得把 worker 函數改回原來的樣子,下面例子是以原版的 worker 為基礎的
六、多線程不阻塞
def 多線程不阻塞(): t1 = threading.Thread(target=worker, args=('張三',)) t2 = threading.Thread(target=worker, args=('李四',)) t1.start() t2.start() print('Finished')
運行結果:
解釋: 參考上面單線程不阻塞↑,不阻塞的情況下,也是會等待全部線程任務執行完成才結束程序的。多線程有一定的並發現象
七、守護線程(后台線程)阻塞
def 守護線程阻塞(): t1 = threading.Thread(daemon=True, target=worker, args=('張三',)) t2 = threading.Thread(daemon=True, target=worker, args=('李四',)) t1.start() t2.start() t1.join() t2.join() print('Finished')
運行結果:
解釋:這里看着上面的普通線程沒有什么區別的,具體的區別看下面↓↓↓
八、守護線程不阻塞
def 守護線程不阻塞(): t1 = threading.Thread(daemon=True, target=worker, args=('張三',)) t2 = threading.Thread(daemon=True, target=worker, args=('李四',)) t1.start() t2.start() print('Finished')
運行結果:
解釋:可以看到和”多線程不阻塞“很不一樣,主程序並不會等待子線程完成任務才結束。而是直接就結束了。這是守護線程(后台線程)的一個特點:不阻塞的情況下,后台線程(守護線程)會在主線程結束的時候自動死掉
九、守護線程不阻塞但主線程比較晚結束
def 守護線程不阻塞但主線程比較晚結束(): t1 = threading.Thread(daemon=True, target=worker, args=('張三',)) t2 = threading.Thread(daemon=True, target=worker, args=('李四',)) t1.start() t2.start() time.sleep(5) print('Finished')
運行結果:
解釋:這里的主線程並沒有等子(守護)線程,只是主線程耗時比子線程還要久,子線程先執行完畢了
十、線程池
def 線程池(): with ThreadPoolExecutor(max_workers=3) as p: p.submit(worker, '張三') p.submit(worker, '李四') print('Finished')
運行結果:
解釋:線程池並沒有 join 方法,但它默認就是阻塞的。運行結果基本和“多線程的正確阻塞”一樣,只是線程池比較方便管理~
本文鏈接:https://www.cnblogs.com/tujia/p/13684251.html
完。