天天看點

線程鎖的使用 | 手把手教你入門Python之一百零二

上一篇: 線程安全 | 手把手教你入門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)狀态。

總結

鎖的好處:

確定了某段關鍵代碼隻能由一個線程從頭到尾完整地執行

鎖的壞處:

  • 阻止了多線程并發執行,包含鎖的某段代碼實際上隻能以單線程模式執行,效率就大大地下降了。
  • 由于可以存在多個鎖,不同的線程持有不同的鎖,并試圖擷取對方持有的鎖時,可能會造成死鎖。

配套視訊