同步就是協同步調,按預定的先後次序進行執行。如:你說完,我再說。
"同"字從字面上容易理解為一起動作
其實不是,"同"字應是指協同、協助、互相配合。
如程序、執行緒同步,可理解為程序或執行緒a和b一塊配合,a執行到一定程度時要依靠b的某個結果,於是停下來,示意b執行;b執行,再將結果給a;a再繼續操作。
解決執行緒同時修改全域性變數的方式
對於上一小節提出的那個計算錯誤的問題,可以通過執行緒同步來進行解決
思路,如下:
系統呼叫t1,然後獲取到g_num的值為0,此時上一把鎖,即不允許其他執行緒操作g_num
t1對g_num的值進行+1
t1解鎖,此時g_num的值為1,其他的執行緒就可以使用g_num了,而且是g_num的值不是0而是1
同理其他執行緒在對g_num進行修改時,都要先上鎖,處理完後再解鎖,在上鎖的整個過程中不允許其他執行緒訪問,就保證了資料的正確性
當多個執行緒幾乎同時修改某乙個共享資料的時候,需要進行同步控制
執行緒同步能夠保證多個執行緒安全訪問競爭資源,最簡單的同步機制是引入互斥鎖。
互斥鎖為資源引入乙個狀態:鎖定/非鎖定
某個執行緒要更改共享資料時,先將其鎖定,此時資源的狀態為「鎖定」,其他執行緒不能更改;直到該執行緒釋放資源,將資源的狀態變成「非鎖定」,其他的執行緒才能再次鎖定該資源。互斥鎖保證了每次只有乙個執行緒進行寫入操作,從而保證了多執行緒情況下資料的正確性。
threading模組中定義了lock類,可以方便的處理鎖定:
lock = threading.lock(
)#建立鎖
lock.acquire(
)#上鎖
lock.release(
)#解鎖
如果這個鎖之前是沒有上鎖的,那麼acquire不會堵塞
如果在呼叫acquire對這個鎖上鎖之前 它已經被 其他執行緒上了鎖,那麼此時acquire會堵塞,直到這個鎖被解鎖為止
使用互斥鎖自加:from threading import thread
from threading import lock
import time
g_num =
0# 建立乙個全域性鎖物件
glock = lock(
)def
work1
(num)
:global g_num
glock.acquire(
)# 給該執行緒加鎖
for i in
range
(num)
: g_num+=
1 glock.release(
)#解鎖
print
("in work1-->"
,g_num)
defwork2
(num)
:global g_num
glock.acquire(
)for i in
range
(num)
: g_num+=
1 glock.release(
)print
("in work2-->"
,g_num)
defmain()
: t1 = thread(target=work1,args=
(1000000,)
) t2 = thread(target=work2,args=
(1000000,)
) t1.start(
) t2.start(
) t2.join(
)if __name__ ==
'__main__'
: main(
)print
("main in-->"
,g_num)
執行結果:
in work1-
->
1000000
in work2-
->
2000000
main in--
>
2000000
from threading import thread
from threading import lock
import time
lock1 = lock(
)# 建立第乙個鎖
lock2 = lock(
)# 建立第二個鎖
defwork1
(num)
: lock1.acquire(
)#lock1上鎖
time.sleep(1)
# 等待一秒鐘
print
("in work1"
) lock2.acquire(
)# lock2上鎖
print
("work1----->"
) lock2.release(
)# 解鎖lock2
lock1.release(
)# 解鎖lock1
defwork2
(num)
: lock2.acquire(
)# 給lock2上鎖
print
("in work2"
) lock1.acquire(
)# 給lock1上鎖
print
("work2---->"
) lock1.release(
)# 進行解鎖
lock2.release(
)if __name__ ==
'__main__'
: t1 = thread(target=work1,args=
(100000,)
)# 建立執行緒t1
t2 = thread(target=work2,args=
(100000,)
)# 建立執行緒t2
t1.start(
)# 啟動執行緒
t2.start(
)
執行結果:
避免死鎖
程式設計時要盡量避免(銀行家演算法)
新增超時時間等
附錄-銀行家演算法
[背景知識]
乙個銀行家如何將一定數目的資金安全地借給若干個客戶,使這些客戶既能借到錢完成要幹的事,同時銀行家又能收回全部資金而不至於破產,這就是銀行家問題。這個問題同作業系統中資源分配問題十分相似:銀行家就像乙個作業系統,客戶就像執行的程序,銀行家的資金就是系統的資源。
[問題的描述]
乙個銀行家擁有一定數量的資金,有若干個客戶要貸款。每個客戶須在一開始就宣告他所需貸款的總額。若該客戶貸款總額不超過銀行家的資金總數,銀行家可以接收客戶的要求。客戶貸款是以每次乙個資金單位(如1萬rmb等)的方式進行的,客戶在借滿所需的全部單位款額之前可能會等待,但銀行家須保證這種等待是有限的,可完成的。
例如:有三個客戶c1,c2,c3,向銀行家借款,該銀行家的資金總額為10個資金單位,其中c1客戶要借9各資金單位,c2客戶要借3個資金單位,c3客戶要借8個資金單位,總計20個資金單位。某一時刻的狀態如圖所示。
對於a圖的狀態,按照安全序列的要求,我們選的第乙個客戶應滿足該客戶所需的貸款小於等於銀行家當前所剩餘的錢款,可以看出只有c2客戶能被滿足:c2客戶需1個資金單位,小銀行家手中的2個資金單位,於是銀行家把1個資金單位借給c2客戶,使之完成工作並歸還所借的3個資金單位的錢,進入b圖。同理,銀行家把4個資金單位借給c3客戶,使其完成工作,在c圖中,只剩乙個客戶c1,它需7個資金單位,這時銀行家有8個資金單位,所以c1也能順利借到錢並完成工作。最後(見圖d)銀行家收回全部10個資金單位,保證不賠本。那麼客戶序列就是個安全序列,按照這個序列貸款,銀行家才是安全的。否則的話,若在圖b狀態時,銀行家把手中的4個資金單位借給了c1,則出現不安全狀態:這時c1,c3均不能完成工作,而銀行家手中又沒有錢了,系統陷入僵持局面,銀行家也不能收回投資。
綜上所述,銀行家演算法是從當前狀態出發,逐個按安全序列檢查各客戶誰能完成其工作,然後假定其完成工作且歸還全部貸款,再進而檢查下乙個能完成工作的客戶,…。如果所有客戶都能完成工作,則找到乙個安全序列,銀行家才是安全的。
python 互斥鎖,死鎖
同步與非同步之間的區別 1.同步 可以理解為執行緒a和b一塊配合工作,a執行到一定程度時要依靠b的某個結 果,於是停下來示意b執行,b執行完將結果給a,然後a繼續執行。2.非同步 非同步是指程序不需要一直等待下去,而是繼續執行下面的操作,不管其他程序的狀態,當有資訊返回的時候會通知程序進行處理,這樣...
Python互斥鎖 死鎖
死鎖當多個執行緒幾乎同時修改某乙個共享資料的時候,需要進行同步控制。執行緒同步能夠保證多個執行緒安全訪問競爭資源,最簡單的同步機制是引入互斥鎖。互斥鎖為資源引入乙個狀態 鎖定 非鎖定 某個執行緒要更改共享資料時,先將其鎖定,此時資源的狀態為 鎖定 其他執行緒不能更改 直到該執行緒釋放資源,將資源的狀...
python 互斥鎖 死鎖
在多執行緒下,全域性變數是共享的,但是在兩個執行緒同時使用乙個全域性變數時,會出現這麼乙個情況,當a讀取了全域性變數a,b也讀取全域性變數a,a對a進行了 1操作,但是b是讀取a沒有進行一次 1操作之前的資料,然後b也進行 1操作,這就導致這錯誤的出現。為了防止這個錯誤的出現,所以引進互斥鎖。使用鎖...