上一篇: 線程安全 | 手把手教你入門Python之一百零一 下一篇: 線程間通信 | 手把手教你入門Python之一百零三 本文來自于千鋒教育在阿裡雲開發者社群學習中心上線課程 《Python入門2020最新大課》 ,主講人姜偉。
線程鎖的使用
同步
當多個線程幾乎同時修改某一個共享資料的時候,需要進行同步控制。同步就是協同步調,按預定的先後次序進行運作。線程同步能夠保證多個線程安全通路競争資源,最簡單的同步機制是引入互斥鎖。
互斥鎖
互斥鎖為資源引入一個狀态:鎖定/非鎖定
某個線程要更改共享資料時,先将其鎖定,此時資源的狀态為“鎖定”,其他線程不能更改;直到該線程釋放資源,将資源的狀态變成“非鎖定”,其他的線程才能再次鎖定該資源。互斥鎖保證了每次隻有一個線程進行寫入操作,進而保證了多線程情況下資料的正确性。
threading子產品中定義了Lock類,可以友善的處理鎖定:
# 建立鎖
mutex = threading.Lock()
# 鎖定
mutex.acquire()
# 釋放
mutex.release()
注意:
- 如果這個鎖之前是沒有上鎖的,那麼acquire不會堵塞
- 如果在調用acquire對這個鎖上鎖之前 它已經被 其他線程上了鎖,那麼此時acquire會堵塞,直到這個鎖被解鎖為止。
- 和檔案操作一樣,Lock也可以使用with語句快速的實作打開和關閉操作。
使用互斥鎖解決賣票問題
import threading
import time
ticket = 20
# 建立一把鎖
lock = threading.Lock()
def sell_ticket():
global ticket
while True:
print('呵呵呵')
print('哈哈哈')
print('ddd')
print('ppp')
print('sss')
print('ttt')
print('xxx')
lock.acquire() # 加同步鎖
if ticket > 0:
time.sleep(1)
ticket -= 1
lock.release()
print('{}賣出一張票,還剩{}張'.format(threading.current_thread().name, ticket))
else:
lock.release()
print('票賣完了')
break
t1 = threading.Thread(target=sell_ticket, name='線程1')
t2 = threading.Thread(target=sell_ticket, name='線程2')
t1.start()
t2.start()
上鎖過程:
當一個線程調用鎖的acquire()方法獲得鎖時,鎖就進入“locked”狀态。
每次隻有一個線程可以獲得鎖。如果此時另一個線程試圖獲得這個鎖,該線程就會變為“blocked”狀态,稱為“阻塞”,直到擁有鎖的線程調用鎖的release()方法釋放鎖之後,鎖進入“unlocked”狀态。
線程排程程式從處于同步阻塞狀态的線程中選擇一個來獲得鎖,并使得該線程進入運作(running)狀态。
總結
鎖的好處:
確定了某段關鍵代碼隻能由一個線程從頭到尾完整地執行
鎖的壞處:
- 阻止了多線程并發執行,包含鎖的某段代碼實際上隻能以單線程模式執行,效率就大大地下降了。
- 由于可以存在多個鎖,不同的線程持有不同的鎖,并試圖擷取對方持有的鎖時,可能會造成死鎖。