深入瞭解Python的多線程基礎
線程
線程(Thread
),有時也被稱為輕量級進程(Lightweight Process,LWP
),是操作系統獨⽴調度和分派的基本單位,本質上就是一串指令的集合。
⼀個標準的線程由線程id、當前指令指針(PC),寄存器集合和堆棧組成,它是進程中的⼀個實體,線程本身不擁有系統資源,隻擁有⼀點⼉在運⾏中必不可少的資源(如程序計數器、寄存器、棧),但它可與同屬⼀個進程的其它線程共享進程所擁有的全部資源。線程不能夠獨⽴執⾏,必須依存在進程中。
多線程
多線程就是使用多個線程同時執行任務,實現瞭任務的並行執行,從而提高程序運行效率的方法。
試想一下,如果在單個線程內執行多個任務(比如發送網絡請求等),如果前面的任務比較耗時,而後面的任務需要等待前面的任務執行完才能執行,這樣會影響任務執行效率,那麼就可以使用多線程去執行這些任務,任務可以同時進行,那麼將大大的提高執行效率。
Python多線程
在Python中,提供瞭threading模塊來實現多進程操作,這個模塊是基於較低級的模塊 _thread 的基礎上建立的,提供瞭更易用的高級多線程API。
創建線程
可以通過threading模塊中的Thread類來創建線程對象。
Thread語法結構:
threading.Thread(group, target, name, daemon)
group
:默認為None(該參數是為瞭以後實現ThreadGroup類而保留的)target
:在run方法中調用的可調用對象,即線程要執行的任務name
:線程名稱,可以不設定,默認為”Thread-N”形式的名稱args
:給target指定的函數傳遞的參數,以元組的⽅式傳遞kwargs
:給target指定的函數傳遞命名參數daemon
:默認為None,將顯式地設置該線程是否為守護模式。如果是None,線程將繼承當前線程的守護模式屬性
Thread常用方法
start()
:啟動線程,並調用該線程中的run()方法run()
:線程啟動時運行的方法,正是它去調用target指定的函數join(timeout=None)
:讓當前調用者線程(一般為主線程)等待,直到該線程結束,timeout是可選的超時時間is_alive()
:返回當前線程是否存活
import threading import time def work(i): print("子線程'{}'work正在運行......".format(threading.current_thread().name)) time.sleep(i) print("子線程'{}'運行結束......".format(threading.current_thread().name)) if __name__ == '__main__': print("主線程{}啟動".format(threading.current_thread().name)) # 獲取線程的名稱 threads = [] for i in range(5): t = threading.Thread(target=work, args=(i,)) # 啟動線程 threads.append(t) t.start() for t in threads: t.join() print("主線程結束")
執行結果為:
上述代碼中使用t.join()的功能就是讓主線程等待所有子線程結束後才結束,如果想設置守護線程(主線程結束,子線程也隨之結束,無論任務執行完成與否)的話,可以使用t.daemon = True
。
GIL鎖
GIL的全稱是Global Interpreter Lock
(全局解釋器鎖),這個鎖最初的設計是為瞭保證同一份數據不能被多個線程同時修改,每個線程在執行任務的時候都需要先獲取GIL,保證同一時刻隻有一個線程可以執行,即同一時刻隻有一個線程在解釋器中運行,因此Python中的多線程是假的多線程,不是真正意義上的多線程。 如果程序中有多個線程執行任務,那麼多個線程會被解釋器輪流執行,隻不過是切換的很快、很頻繁,給人一種多線程“同時”在執行的錯覺。
線程池
在之前的文章說過,進程有進程池的機制,同樣,線程也有線程池。線程池可以在程序啟動時就創建自定義數量的空閑的線程,程序隻要將一個任務提交給線程池,線程池就會啟動一個空閑的線程來執行它。當該任務執行結束後,該線程並不會死亡,而是再次返回到線程池中變成空閑狀態,等待下一個任務的執行。
multiprocessing.dummy
裡面也有一個Pool對象,它其實就是線程的封裝,使用起來和multiprocessing
的Pool非常類似。它們api都是通用的,簡單地說,multiprocessing.dummy
是multiprocessing
進程池模塊復制的一個線程池模塊,強調一下,這裡線程池也是受到GIL限制的。
使用方式和multiprocessing.Pool一致,具體參考Python進程池。
from multiprocessing.dummy import Pool import time def work(i): print("work'{}'執行中......".format(i)) time.sleep(2) print("work'{}'執行完畢......".format(i)) if __name__ == '__main__': # 創建線程池 # Pool(5) 表示創建容量為5個線程的線程池 pool = Pool(5) for i in range(10): pool.apply_async(work, (i, )) pool.close() pool.join()
總結
由於Python中的多線程受GIL鎖的限制,導致不能利用機器多核的特性,隻能利用單核,是假的多線程,但是也不是一無是處,對於IO密集型任務,多線程是能夠有效提升運行效率的,這是因為單線程下有IO操作時,會進行IO等待,這樣會浪費等待的這段時間,而開啟多線程能在線程A等待時,自動切換到線程B,可以減少不必要的時間浪費,從而能提升程序運行效率,但是也不是最好的選擇,對於處理IO密集型任務,在Python還有更好的選擇協程,在後續文章會介紹。
本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!
推薦閱讀:
- Python多線程即相關理念詳解
- Python解析器Cpython的GIL解釋器鎖工作機制
- python 多線程實現多任務的方法示例
- Python多線程編程入門詳解
- python threading模塊的使用指南