淺談Python線程的同步互斥與死鎖
線程間通信方法
1. 通信方法
線程間使用全局變量進(jìn)行通信
2. 共享資源爭(zhēng)奪
共享資源:多個(gè)進(jìn)程或者線程都可以操作的資源稱為共享資源。對(duì)共享資源的操作代碼段稱為臨界區(qū)。
影響 : 對(duì)共享資源的無序操作可能會(huì)帶來數(shù)據(jù)的混亂,或者操作錯(cuò)誤。此時(shí)往往需要同步互斥機(jī)制協(xié)調(diào)操作順序。
3. 同步互斥機(jī)制
同步 : 同步是一種協(xié)作關(guān)系,為完成操作,多進(jìn)程或者線程間形成一種協(xié)調(diào),按照必要的步驟有序執(zhí)行操作。兩個(gè)或兩個(gè)以上的進(jìn)程或線程在運(yùn)行過程中協(xié)同步調(diào),按預(yù)定的先后次序運(yùn)行。比如 A 任務(wù)的運(yùn)行依賴于 B 任務(wù)產(chǎn)生的數(shù)據(jù)。
互斥 : 互斥是一種制約關(guān)系,當(dāng)一個(gè)進(jìn)程或者線程占有資源時(shí)會(huì)進(jìn)行加鎖處理,此時(shí)其他進(jìn)程線程就無法操作該資源,直到解鎖后才能操作。一個(gè)公共資源同一時(shí)刻只能被一個(gè)進(jìn)程或線程使用,多個(gè)進(jìn)程或線程不能同時(shí)使用公共資源
線程同步互斥方法
線程Event同步
from threading import Event e = Event() 創(chuàng)建線程event對(duì)象 e.wait([timeout]) 阻塞等待e被set e.set() 設(shè)置e,使wait結(jié)束阻塞 e.clear() 使e回到未被設(shè)置狀態(tài) e.is_set() 查看當(dāng)前e是否被設(shè)置
示例:
import time import threading event = threading.Event() # 紅綠燈 def lighter(): count = 0 event.set() # 剛進(jìn)來的時(shí)候是綠燈 while True: if 4 < count < 10: event.clear() # 清除設(shè)置,阻塞等待 print("[信號(hào)燈]:紅,不能通行", count) elif count >= 10: # 添加設(shè)置,繼續(xù)執(zhí)行 event.set() count = 0 else: event.set() # 添加設(shè)置,繼續(xù)執(zhí)行 print("[信號(hào)燈]:綠燈,可以通行", count) time.sleep(1) count += 1 # 汽車 def car(name): while True: if event.is_set(): print("{0}: 綠燈 , 走起...".format(name)) time.sleep(1) else: print("{0}: 紅燈 , 停車...".format(name)) event.wait() print("{0}: 綠燈亮了 , 繼續(xù)前進(jìn)...".format(name)) light = threading.Thread(target=lighter, ) light.start() car1 = threading.Thread(target=car, args=("小跑",)) car1.start()
線程鎖 Lock
from threading import Lock lock = Lock() #創(chuàng)建鎖對(duì)象 lock.acquire() #上鎖 如果lock已經(jīng)上鎖再調(diào)用會(huì)阻塞 lock.release() #解鎖
with lock: 上鎖
with代碼塊結(jié)束自動(dòng)解鎖
示例:
from threading import Thread, Lock from time import sleep a = b = 0 lock = Lock() # 子線程輸出a b def value(): while True: lock.acquire() # 上鎖 if a != b: print("a = %d,b = %d" % (a, b)) lock.release() # 解鎖 t = Thread(target=value) t.start() # 主線程加鎖更改a b時(shí)候,子線程處理a b 時(shí)也要進(jìn)行加鎖,重復(fù)加鎖就會(huì)阻塞等待主線程處理結(jié)束 # 同理主進(jìn)程再次更改a b 時(shí)等 子進(jìn)程結(jié)束才可以 while True: with lock: # 自動(dòng)上/解鎖 a += 1 b += 1 t.join
死鎖及其處理
1. 定義
死鎖是指兩個(gè)或兩個(gè)以上的線程在執(zhí)行過程中,由于競(jìng)爭(zhēng)資源或者由于彼此通信而造成的一種阻塞的現(xiàn)象,若無外力作用,它們都將無法推進(jìn)下去。此時(shí)稱系統(tǒng)處于死鎖狀態(tài)或系統(tǒng)產(chǎn)生了死鎖。
2. 死鎖產(chǎn)生條件
【互斥條件】:指線程對(duì)所分配到的資源進(jìn)行排它性使用,即在一段時(shí)間內(nèi)某資源只由一個(gè)進(jìn)程占用。如果此時(shí)還有其它進(jìn)程請(qǐng)求資源,則請(qǐng)求者只能等待,直至占有資源的進(jìn)程用畢釋放。
【請(qǐng)求和保持條件】:指線程已經(jīng)保持至少一個(gè)資源,但又提出了新的資源請(qǐng)求,而該資源已被其它進(jìn)程占有,此時(shí)請(qǐng)求線程阻塞,但又對(duì)自己已獲得的其它資源保持不放。
【不剝奪條件】:指線程已獲得的資源,在未使用完之前,不能被剝奪,只能在使用完時(shí)由自己釋放,通常CPU內(nèi)存資源是可以被系統(tǒng)強(qiáng)行調(diào)配剝奪的。
【環(huán)路等待條件】:指在發(fā)生死鎖時(shí),必然存在一個(gè)線程——資源的環(huán)形鏈,即進(jìn)程集合{T0,T1,T2,···,Tn}中的T0正在等待一個(gè)T1占用的資源;T1正在等待T2占用的資源,……,Tn正在等待已被T0占用的資源。
簡(jiǎn)單來說造成死鎖的原因可以概括成三句話:
【1】當(dāng)前線程擁有其他線程需要的資源
【2】當(dāng)前線程等待其他線程已擁有的資源
【3】都不放棄自己擁有的資源
T1擁有R1,T2擁有R2。T1請(qǐng)求使用R2,T2請(qǐng)求使用R1,但是T1,T2 都不愿釋放R1,R2,互相一直等待下去,造成死鎖
3. 如何避免死鎖
死鎖是我們非常不愿意看到的一種現(xiàn)象,我們要盡可能避免死鎖的情況發(fā)生。通過設(shè)置某些限制條件,去破壞產(chǎn)生死鎖的四個(gè)必要條件中的一個(gè)或者幾個(gè),來預(yù)防發(fā)生死鎖。預(yù)防死鎖是一種較易實(shí)現(xiàn)的方法。但是由于所施加的限制條件往往太嚴(yán)格,可能會(huì)導(dǎo)致系統(tǒng)資源利用率。
from threading import Lock, Thread # 交易類 class Account: def __init__(self, _id, balance, lock): self.id = _id self.balance = balance self.lock = lock # 各自賬戶鎖 # 取錢 def withdraw(self, amount): self.balance -= amount # 存錢 def deposit(self, amount): self.balance += amount # 查看賬戶 def get_balance(self): return self.balance # 轉(zhuǎn)賬 def transfer(from_, to, amount): if from_.lock.acquire(): # 鎖住自己的賬戶 from_.withdraw(amount) # 自己賬戶減少 if to.lock.acquire(): # 鎖住對(duì)方賬戶 to.deposit(amount) # 對(duì)方賬戶增加 to.lock.release() # 解鎖對(duì)方賬戶 from_.lock.release() # 自己賬戶解鎖 print("轉(zhuǎn)賬完成") Abby = Account("Abby", 5000, Lock()) Balen = Account("Balen", 3000, Lock()) t = Thread(target=transfer, args=(Abby, Balen, 1000)) t2 = Thread(target=transfer, args=(Balen, Abby, 500)) t.start() t2.start() t.join() t2.join() print("Abby:", Abby.get_balance()) print("Balen:", Balen.get_balance())
到此這篇關(guān)于淺談Python線程的同步互斥與死鎖的文章就介紹到這了,更多相關(guān)Python線程同步互斥與死鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python 提取dict轉(zhuǎn)換為xml/json/table并輸出的實(shí)現(xiàn)代碼
這篇文章主要介紹了Python 提取dict轉(zhuǎn)換為xml/json/table并輸出的實(shí)現(xiàn)代碼,需要的朋友可以參考下2016-08-08Python利用cv2動(dòng)態(tài)繪制圓和矩形的示例詳解
這篇文章主要為大家詳細(xì)介紹了Python如何利用cv2實(shí)現(xiàn)動(dòng)態(tài)繪制圓和矩形的功能,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,需要的可以參考一下2023-03-03python利用Appium實(shí)現(xiàn)自動(dòng)控制移動(dòng)設(shè)備并提取數(shù)據(jù)功能
這篇文章主要介紹了python利用Appium自動(dòng)控制移動(dòng)設(shè)備并提取數(shù)據(jù),本文以控制抖音app滑動(dòng)并獲取抖音短視頻發(fā)布者為例,通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-09-09python獲取當(dāng)前用戶的主目錄路徑方法(推薦)
下面小編就為大家?guī)硪黄猵ython獲取當(dāng)前用戶的主目錄路徑方法(推薦)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-01-01淺談python內(nèi)置函數(shù)callable的用法
這篇文章主要介紹了淺談python內(nèi)置函數(shù)callable的用法, callable函數(shù)可用于判斷一個(gè)對(duì)象是否可以被調(diào)用,若對(duì)象可以被調(diào)用則返回True,反之則返回False,需要的朋友可以參考下2023-04-04pytorch實(shí)現(xiàn)建立自己的數(shù)據(jù)集(以mnist為例)
今天小編就為大家分享一篇pytorch實(shí)現(xiàn)建立自己的數(shù)據(jù)集(以mnist為例),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-01-01