深入了解Python的多線(xiàn)程基礎(chǔ)
線(xiàn)程
線(xiàn)程(Thread),有時(shí)也被稱(chēng)為輕量級(jí)進(jìn)程(Lightweight Process,LWP),是操作系統(tǒng)獨(dú)?調(diào)度和分派的基本單位,本質(zhì)上就是一串指令的集合。
?個(gè)標(biāo)準(zhǔn)的線(xiàn)程由線(xiàn)程id、當(dāng)前指令指針(PC),寄存器集合和堆棧組成,它是進(jìn)程中的?個(gè)實(shí)體,線(xiàn)程本身不擁有系統(tǒng)資源,只擁有?點(diǎn)?在運(yùn)?中必不可少的資源(如程序計(jì)數(shù)器、寄存器、棧),但它可與同屬?個(gè)進(jìn)程的其它線(xiàn)程共享進(jìn)程所擁有的全部資源。線(xiàn)程不能夠獨(dú)?執(zhí)?,必須依存在進(jìn)程中。
多線(xiàn)程
多線(xiàn)程就是使用多個(gè)線(xiàn)程同時(shí)執(zhí)行任務(wù),實(shí)現(xiàn)了任務(wù)的并行執(zhí)行,從而提高程序運(yùn)行效率的方法。
試想一下,如果在單個(gè)線(xiàn)程內(nèi)執(zhí)行多個(gè)任務(wù)(比如發(fā)送網(wǎng)絡(luò)請(qǐng)求等),如果前面的任務(wù)比較耗時(shí),而后面的任務(wù)需要等待前面的任務(wù)執(zhí)行完才能執(zhí)行,這樣會(huì)影響任務(wù)執(zhí)行效率,那么就可以使用多線(xiàn)程去執(zhí)行這些任務(wù),任務(wù)可以同時(shí)進(jìn)行,那么將大大的提高執(zhí)行效率。
Python多線(xiàn)程
在Python中,提供了threading模塊來(lái)實(shí)現(xiàn)多進(jìn)程操作,這個(gè)模塊是基于較低級(jí)的模塊 _thread 的基礎(chǔ)上建立的,提供了更易用的高級(jí)多線(xiàn)程API。
創(chuàng)建線(xiàn)程
可以通過(guò)threading模塊中的Thread類(lèi)來(lái)創(chuàng)建線(xiàn)程對(duì)象。
Thread語(yǔ)法結(jié)構(gòu):
threading.Thread(group, target, name, daemon)
group:默認(rèn)為None(該參數(shù)是為了以后實(shí)現(xiàn)ThreadGroup類(lèi)而保留的)target:在run方法中調(diào)用的可調(diào)用對(duì)象,即線(xiàn)程要執(zhí)行的任務(wù)name:線(xiàn)程名稱(chēng),可以不設(shè)定,默認(rèn)為"Thread-N"形式的名稱(chēng)args:給target指定的函數(shù)傳遞的參數(shù),以元組的?式傳遞kwargs:給target指定的函數(shù)傳遞命名參數(shù)daemon:默認(rèn)為None,將顯式地設(shè)置該線(xiàn)程是否為守護(hù)模式。如果是None,線(xiàn)程將繼承當(dāng)前線(xiàn)程的守護(hù)模式屬性
Thread常用方法
start():?jiǎn)?dòng)線(xiàn)程,并調(diào)用該線(xiàn)程中的run()方法run():線(xiàn)程啟動(dòng)時(shí)運(yùn)行的方法,正是它去調(diào)用target指定的函數(shù)join(timeout=None):讓當(dāng)前調(diào)用者線(xiàn)程(一般為主線(xiàn)程)等待,直到該線(xiàn)程結(jié)束,timeout是可選的超時(shí)時(shí)間is_alive():返回當(dāng)前線(xiàn)程是否存活
import threading
import time
def work(i):
print("子線(xiàn)程'{}'work正在運(yùn)行......".format(threading.current_thread().name))
time.sleep(i)
print("子線(xiàn)程'{}'運(yùn)行結(jié)束......".format(threading.current_thread().name))
if __name__ == '__main__':
print("主線(xiàn)程{}啟動(dòng)".format(threading.current_thread().name))
# 獲取線(xiàn)程的名稱(chēng)
threads = []
for i in range(5):
t = threading.Thread(target=work, args=(i,))
# 啟動(dòng)線(xiàn)程
threads.append(t)
t.start()
for t in threads:
t.join()
print("主線(xiàn)程結(jié)束")
執(zhí)行結(jié)果為:

上述代碼中使用t.join()的功能就是讓主線(xiàn)程等待所有子線(xiàn)程結(jié)束后才結(jié)束,如果想設(shè)置守護(hù)線(xiàn)程(主線(xiàn)程結(jié)束,子線(xiàn)程也隨之結(jié)束,無(wú)論任務(wù)執(zhí)行完成與否)的話(huà),可以使用t.daemon = True。
GIL鎖
GIL的全稱(chēng)是Global Interpreter Lock(全局解釋器鎖),這個(gè)鎖最初的設(shè)計(jì)是為了保證同一份數(shù)據(jù)不能被多個(gè)線(xiàn)程同時(shí)修改,每個(gè)線(xiàn)程在執(zhí)行任務(wù)的時(shí)候都需要先獲取GIL,保證同一時(shí)刻只有一個(gè)線(xiàn)程可以執(zhí)行,即同一時(shí)刻只有一個(gè)線(xiàn)程在解釋器中運(yùn)行,因此Python中的多線(xiàn)程是假的多線(xiàn)程,不是真正意義上的多線(xiàn)程。 如果程序中有多個(gè)線(xiàn)程執(zhí)行任務(wù),那么多個(gè)線(xiàn)程會(huì)被解釋器輪流執(zhí)行,只不過(guò)是切換的很快、很頻繁,給人一種多線(xiàn)程“同時(shí)”在執(zhí)行的錯(cuò)覺(jué)。
線(xiàn)程池
在之前的文章說(shuō)過(guò),進(jìn)程有進(jìn)程池的機(jī)制,同樣,線(xiàn)程也有線(xiàn)程池。線(xiàn)程池可以在程序啟動(dòng)時(shí)就創(chuàng)建自定義數(shù)量的空閑的線(xiàn)程,程序只要將一個(gè)任務(wù)提交給線(xiàn)程池,線(xiàn)程池就會(huì)啟動(dòng)一個(gè)空閑的線(xiàn)程來(lái)執(zhí)行它。當(dāng)該任務(wù)執(zhí)行結(jié)束后,該線(xiàn)程并不會(huì)死亡,而是再次返回到線(xiàn)程池中變成空閑狀態(tài),等待下一個(gè)任務(wù)的執(zhí)行。
multiprocessing.dummy里面也有一個(gè)Pool對(duì)象,它其實(shí)就是線(xiàn)程的封裝,使用起來(lái)和multiprocessing的Pool非常類(lèi)似。它們api都是通用的,簡(jiǎn)單地說(shuō),multiprocessing.dummy是multiprocessing進(jìn)程池模塊復(fù)制的一個(gè)線(xiàn)程池模塊,強(qiáng)調(diào)一下,這里線(xiàn)程池也是受到GIL限制的。
使用方式和multiprocessing.Pool一致,具體參考Python進(jìn)程池。
from multiprocessing.dummy import Pool
import time
def work(i):
print("work'{}'執(zhí)行中......".format(i))
time.sleep(2)
print("work'{}'執(zhí)行完畢......".format(i))
if __name__ == '__main__':
# 創(chuàng)建線(xiàn)程池
# Pool(5) 表示創(chuàng)建容量為5個(gè)線(xiàn)程的線(xiàn)程池
pool = Pool(5)
for i in range(10):
pool.apply_async(work, (i, ))
pool.close()
pool.join()
總結(jié)
由于Python中的多線(xiàn)程受GIL鎖的限制,導(dǎo)致不能利用機(jī)器多核的特性,只能利用單核,是假的多線(xiàn)程,但是也不是一無(wú)是處,對(duì)于IO密集型任務(wù),多線(xiàn)程是能夠有效提升運(yùn)行效率的,這是因?yàn)閱尉€(xiàn)程下有IO操作時(shí),會(huì)進(jìn)行IO等待,這樣會(huì)浪費(fèi)等待的這段時(shí)間,而開(kāi)啟多線(xiàn)程能在線(xiàn)程A等待時(shí),自動(dòng)切換到線(xiàn)程B,可以減少不必要的時(shí)間浪費(fèi),從而能提升程序運(yùn)行效率,但是也不是最好的選擇,對(duì)于處理IO密集型任務(wù),在Python還有更好的選擇協(xié)程,在后續(xù)文章會(huì)介紹。
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
Python使用BeautifulSoup解析并獲取圖片的實(shí)戰(zhàn)分享
這篇文章主要介紹了Python使用BeautifulSoup解析并獲取圖片的實(shí)戰(zhàn)分享,文中通過(guò)代碼和圖文結(jié)合的方式給大家講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-06-06
Python?Fire中兩種命令行參數(shù)靈活設(shè)置方式詳解
Python的Fire庫(kù),一個(gè)用來(lái)生成命令行工具的的庫(kù),這篇文章主要針對(duì)命令行參數(shù),補(bǔ)充兩種更加靈活的設(shè)置方式,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-01-01
Python機(jī)器學(xué)習(xí)庫(kù)scikit-learn入門(mén)開(kāi)發(fā)示例
scikit-learn是一個(gè)開(kāi)源Python語(yǔ)言機(jī)器學(xué)習(xí)工具包,它涵蓋了幾乎所有主流機(jī)器學(xué)習(xí)算法的實(shí)現(xiàn),并且提供了一致的調(diào)用接口。它基于Numpy和scipy等Python數(shù)值計(jì)算庫(kù),提供了高效的算法實(shí)現(xiàn)2022-07-07
淺析如何在Python中使用結(jié)構(gòu)模式匹配
在Python 3.10中引入了模式匹配語(yǔ)法,允許我們?cè)趹?yīng)用程序中使用強(qiáng)大的新編程技術(shù)進(jìn)行決策,下面我們就來(lái)講講如何在Python中使用結(jié)構(gòu)模式匹配吧2023-08-08
python獲得linux下所有掛載點(diǎn)(mount points)的方法
這篇文章主要介紹了python獲得linux下所有掛載點(diǎn)(mount points)的方法,涉及Python操作Linux下掛載點(diǎn)的相關(guān)技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-04-04
Python繪制動(dòng)態(tài)水球圖過(guò)程詳解
這篇文章主要介紹了Python繪制動(dòng)態(tài)水球圖過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06
用Python 爬取貓眼電影數(shù)據(jù)分析《無(wú)名之輩》
這篇文章主要介紹了用Python 爬取貓眼電影數(shù)據(jù)分析《無(wú)名之輩》,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
python使用pypdf2實(shí)現(xiàn)pdf文檔解密
利用pypdf2完成pdf的解密,這里的事例是python3環(huán)境下的,當(dāng)然python2下也可以運(yùn)行,只需要修改名稱(chēng)即可,文中通過(guò)代碼示例給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-12-12
OpenCV根據(jù)面積篩選連通域?qū)W習(xí)示例
這篇文章主要為大家介紹了OpenCV根據(jù)面積篩選連通域?qū)W習(xí)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06

