Python多線程操作之互斥鎖、遞歸鎖、信號(hào)量、事件實(shí)例詳解
本文實(shí)例講述了Python多線程操作之互斥鎖、遞歸鎖、信號(hào)量、事件。分享給大家供大家參考,具體如下:
互斥鎖:
- 為什么要有互斥鎖:由于多線程是并行的,如果某一線程取出了某一個(gè)數(shù)據(jù)將要進(jìn)行操作,但它還沒有那么快執(zhí)行完操作,這時(shí)候如果另外一個(gè)線程也要操作這個(gè)數(shù)據(jù),那么這個(gè)數(shù)據(jù)可能會(huì)因?yàn)閮纱尾僮鞫l(fā)生錯(cuò)誤
import time,threading x=6 def run1(): print("run1我拿到了數(shù)據(jù):",x) print("我現(xiàn)在還不想操作,先睡一下") time.sleep(3) print("再看一下數(shù)據(jù),穩(wěn)一穩(wěn)",x) def run2(): global x print("run2我拿到了數(shù)據(jù):", x) x=5 print(x) t1=threading.Thread(target=run1) t2=threading.Thread(target=run2) t1.start() t2.start() t1.join() t2.join()
- 而多線程的互斥鎖機(jī)制本質(zhì)上是:申請(qǐng)一個(gè)鎖,A線程拿了鑰匙【acquire】之后,如果B也想拿到鑰匙是不行的,只有等A把鑰匙還回來【release】才行
- 如何使用互斥鎖:
- 定義一個(gè)鎖對(duì)象:鎖對(duì)象=threading.Lock()
- 請(qǐng)求鎖:鎖對(duì)象.acquire()
- 釋放鎖:鎖對(duì)象.release()
使用互斥鎖來更改上段代碼
import time,threading x=6 def run1(): lock.acquire() global x print("run1我拿到了數(shù)據(jù),x=",x) print("我現(xiàn)在還不想操作,先睡一下") time.sleep(3) print("再看一下數(shù)據(jù),穩(wěn)一穩(wěn),x=",x) x+=1 print("run1操作完畢:x=",x) lock.release() def run2(): lock.acquire() global x print("run2我拿到了數(shù)據(jù):", x) x+=1 print("run2操作完畢:x=",x) lock.release() lock=threading.Lock()#生成一個(gè)鎖對(duì)象 t1=threading.Thread(target=run1) t2=threading.Thread(target=run2) t1.start() t2.start() start_time=time.time() t1.join() t2.join() print("最終的x=",x) print(time.time()-start_time)#3.0多說明,由于受到鎖的影響,run2要等待run1釋放lock,所以變成了串行
這種互斥鎖在操作系統(tǒng)中可以稱作“臨界區(qū)”,如果想了解更多:
https://baike.baidu.com/item/%E4%B8%B4%E7%95%8C%E5%8C%BA/8942134?fr=aladdin
遞歸鎖:
- 為什么要有遞歸鎖:互斥鎖本質(zhì)上是阻止其他線程進(jìn)入,如果有兩個(gè)需要阻止其他線程進(jìn)入的操作【像兩個(gè)人過獨(dú)木橋】,那么需要兩個(gè)鎖,而想要鎖上第二個(gè)如果直接用第一個(gè)鎖的acquire會(huì)失敗,因?yàn)榈谝粋€(gè)鎖還沒release,我們可以選擇再定義一個(gè)互斥鎖對(duì)象來acquire,但這僅僅是兩層的情況下,如果多層的吧,那么就需要定義好幾個(gè)互斥鎖對(duì)象了【而且由于對(duì)象變多,有時(shí)候會(huì)因?yàn)榛ハ嗾{(diào)用鎖而發(fā)生死鎖】。遞歸鎖就是為了處理這種情況,遞歸鎖對(duì)象允許多次acquire和多次release
- 發(fā)生死鎖的情況[A拿到A鎖,想要拿B鎖,B拿著B鎖,想要A鎖]
【以過獨(dú)木橋?yàn)槔浚簶蛑荒苋菀粋€(gè)人通過,A只能看得到北邊橋上有沒有人,看不到南邊橋有沒有人,當(dāng)他看到北邊橋沒人就會(huì)過橋,等到他到橋中間才能看到南邊橋有沒有人,B情況相反:【于是當(dāng)兩個(gè)人一起過橋的時(shí)候就會(huì)發(fā)生死鎖】
import threading,time """ A只能看得到北邊橋上有沒有人,看不到南邊橋有沒有人, 當(dāng)他看到北邊橋沒人就會(huì)過橋,等到他到橋中間才能看到南邊橋有沒有人 """ def A(): lockNorth.acquire()#拿到北邊橋的鎖 print("A過橋北") time.sleep(3)#過橋中 lockSorth.acquire()#企圖過到南邊橋, print("A過橋南") time.sleep(3) # 過橋中 lockSorth.release() lockNorth.release() print("A過橋成功") """ B只能看得到南邊橋上有沒有人,看不到北邊橋有沒有人, 當(dāng)他看到南邊橋沒人就會(huì)過橋,等到他到橋中間才能看到北邊橋有沒有人 """ def B(): lockSorth.acquire() # 企圖過到南邊橋, print("B過橋南") time.sleep(3) # 過橋中 lockNorth.acquire() # 拿到北邊橋的鎖 print("B過橋北") time.sleep(3) # 過橋中 lockNorth.release() lockSorth.release() print("B過橋成功") lockNorth=threading.Lock() lockSorth=threading.Lock() tA=threading.Thread(target=A) tB=threading.Thread(target=B) tA.start() tB.start() tA.join() tB.join()
- 遞歸鎖的本質(zhì)是:本質(zhì)上還是一個(gè)鎖,但如果在一個(gè)線程里面可以多次acquire。【因?yàn)橹挥幸粋€(gè)鎖,所以不會(huì)發(fā)生互相調(diào)用的死鎖,而因?yàn)榭梢远啻握{(diào)用,所以可以鎖多次】
- 如何使用遞歸鎖:
- 定義一個(gè)鎖對(duì)象:遞歸鎖對(duì)象=threading.RLock()
- 請(qǐng)求鎖:鎖對(duì)象.acquire()
- 釋放鎖:鎖對(duì)象.release()
使用遞歸鎖來解決上面的死鎖問題:
import threading,time """ A只能看得到北邊橋上有沒有人,看不到南邊橋有沒有人, 當(dāng)他看到北邊橋沒人就會(huì)過橋,等到他到橋中間才能看到南邊橋有沒有人 """ def A(): lock.acquire()#拿到北邊橋的鎖 print("A過橋北") time.sleep(3)#過橋中 lock.acquire()#企圖過到南邊橋, print("A過橋南") time.sleep(3) # 過橋中 lock.release() lock.release() print("A過橋成功") """ B只能看得到南邊橋上有沒有人,看不到北邊橋有沒有人, 當(dāng)他看到南邊橋沒人就會(huì)過橋,等到他到橋中間才能看到北邊橋有沒有人 """ def B(): lock.acquire() # 拿南橋鎖, print("B過橋南") time.sleep(3) # 過橋中 lock.acquire() # 企圖拿北橋的鎖 print("B過橋北") time.sleep(3) # 過橋中 lock.release() lock.release() print("B過橋成功") lock=threading.RLock() tA=threading.Thread(target=A) tB=threading.Thread(target=B) tA.start() tB.start() tA.join() tB.join()
【由于本質(zhì)是一把鎖,A拿到鎖后,B要等待】
信號(hào)量:
- 什么是信號(hào)量:
信號(hào)量可以限制進(jìn)入的線程的數(shù)量。
- 如何使用信號(hào)量:
- 創(chuàng)建信號(hào)量對(duì)象:信號(hào)量對(duì)象=threading.BoundedSemaphore(x),x是限制進(jìn)程的數(shù)量
- 當(dāng)有進(jìn)程需要進(jìn)入的時(shí)候,調(diào)用acquire()來減少信號(hào)量:信號(hào)量對(duì)象.acquire()
- 當(dāng)有進(jìn)程離開的時(shí)候,調(diào)用release()來增加信號(hào)量:信號(hào)量對(duì)象.release()
import threading,time def run(): s.acquire() print("hello") time.sleep(1.5) s.release() s=threading.BoundedSemaphore(3)#限制3個(gè) threading_list=[] for i in range(12):#創(chuàng)建12個(gè)線程 obj=threading.Thread(target=run) obj.setDaemon(True) # 設(shè)置守護(hù)線程,避免干擾主線程運(yùn)行,并行等待 obj.start() for i in range(4): print("")#為了把結(jié)果分割,可以清楚看出分為了三組 time.sleep(1.5) #結(jié)果分為三組是因?yàn)檫\(yùn)行的太快了,三個(gè)線程裝入的時(shí)間差太小
事件:
- 什么是事件:當(dāng)發(fā)生線程發(fā)生一件事的時(shí)候如果要提醒另外一個(gè)線程,使用事件。雙方共享該事件對(duì)象【等待的一方會(huì)阻塞而進(jìn)行等待】,當(dāng)一方更改事件對(duì)象的時(shí)候,另外一方也能知道【以讀者-寫者為例:讀者要等寫者告訴他去讀才會(huì)去讀,寫者寫完后要設(shè)置一個(gè)事件,當(dāng)該事件設(shè)置時(shí),讀者就會(huì)來讀】
- 如何使用事件:
- 創(chuàng)建事件對(duì)象:事件對(duì)象=threading.Event()
- 設(shè)置事件:事件對(duì)象.set() 判斷事件是否set:事件對(duì)象.is_set(),等待事件set:事件對(duì)象.wait()
- 清除事件:事件對(duì)象.clear()
import threading,time def read(): while True: if event.is_set(): print("事件已設(shè)置,我要讀了!!!!") time.sleep(1) else:#事件未設(shè)置 print("還沒寫好,我要等咯") event.wait()#那么就等著咯 #如果等到了 print("終于等到了!那么我又可以讀了") time.sleep(1) def write(): event.clear()#初始設(shè)空 while True: time.sleep(3)#寫 event.set()#設(shè)置事件,一旦set,那么讀者wait就有返回了,讀者可以繼續(xù)運(yùn)行了 print("write:寫好了") time.sleep(2)#等人讀 event.clear()#清除事件 event=threading.Event() #創(chuàng)建事件對(duì)象 t1=threading.Thread(target=write) t2=threading.Thread(target=read) t1.start() t2.start() t1.join() t2.join() """結(jié)果顯示:讀者確實(shí)一直在等待寫者寫好"""
更多關(guān)于Python相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Python進(jìn)程與線程操作技巧總結(jié)》、《Python數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Python函數(shù)使用技巧總結(jié)》、《Python字符串操作技巧匯總》、《Python入門與進(jìn)階經(jīng)典教程》、《Python+MySQL數(shù)據(jù)庫(kù)程序設(shè)計(jì)入門教程》及《Python常見數(shù)據(jù)庫(kù)操作技巧匯總》
希望本文所述對(duì)大家Python程序設(shè)計(jì)有所幫助。
- 對(duì)python多線程中互斥鎖Threading.Lock的簡(jiǎn)單應(yīng)用詳解
- Python實(shí)現(xiàn)的多線程同步與互斥鎖功能示例
- Python多線程編程(四):使用Lock互斥鎖
- Python多線程編程(五):死鎖的形成
- Python多線程編程(六):可重入鎖RLock
- 詳解python多線程、鎖、event事件機(jī)制的簡(jiǎn)單使用
- 對(duì)python多線程中Lock()與RLock()鎖詳解
- Python多線程編程之多線程加鎖操作示例
- Python 多線程不加鎖分塊讀取文件的方法
- python多線程高級(jí)鎖condition簡(jiǎn)單用法示例
- Python的互斥鎖與信號(hào)量詳解
- Python3.X 線程中信號(hào)量的使用方法示例
相關(guān)文章
使用python實(shí)現(xiàn)哈希表、字典、集合操作
這篇文章主要介紹了使用python實(shí)現(xiàn)哈希表、字典、集合操作,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-12-12selenium?UI自動(dòng)化實(shí)戰(zhàn)過程記錄
如果大家有做過web的自動(dòng)化測(cè)試,相信對(duì)于selenium一定不陌生,測(cè)試人員經(jīng)常使用它來進(jìn)行自動(dòng)化測(cè)試,下面這篇文章主要給大家介紹了關(guān)于selenium?UI自動(dòng)化實(shí)戰(zhàn)的相關(guān)資料,需要的朋友可以參考下2021-12-12Python實(shí)現(xiàn)制作透明背景的電子印章
這篇文章主要為大家詳細(xì)介紹了如何利用Python語(yǔ)言實(shí)現(xiàn)制作透明背景的電子印章,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以嘗試一下2022-09-09PyCharm 2020.1版安裝破解注冊(cè)碼永久激活(激活到2089年)
這篇文章主要介紹了PyCharm 2020.1版安裝破解注冊(cè)碼永久激活(激活到2089年),需要的朋友可以參考下2020-09-09對(duì)python中的os.getpid()和os.fork()函數(shù)詳解
今天小編就為大家分享一篇對(duì)python中的os.getpid()和os.fork()函數(shù)詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-08-08Python使用Quart作為web服務(wù)器的代碼實(shí)現(xiàn)
Quart 是一個(gè)異步的 Web 框架,它使用 ASGI 接口(Asynchronous Server Gateway Interface)而不是傳統(tǒng)的 WSGI,這使得 Quart 特別適合用于構(gòu)建需要處理大量并發(fā)連接的高性能 Web 應(yīng)用程序,本文給大家介紹了Python使用Quart作為web服務(wù)器的代碼實(shí)現(xiàn)2024-06-06python中綁定方法與非綁定方法的實(shí)現(xiàn)示例
本文主要介紹了python中綁定方法與非綁定方法的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09Python中的文件和目錄操作實(shí)現(xiàn)代碼
對(duì)于文件和目錄的處理,雖然可以通過操作系統(tǒng)命令來完成,但是Python語(yǔ)言為了便于開發(fā)人員以編程的方式處理相關(guān)工作,提供了許多處理文件和目錄的內(nèi)置函數(shù)。重要的是,這些函數(shù)無論是在Unix、Windows還是Macintosh平臺(tái)上,它們的使用方式是完全一致的。2011-03-03