1. 程式人生 > >Python並行程式設計(三):執行緒同步Lock

Python並行程式設計(三):執行緒同步Lock

1、基礎概念

      當兩個或以上對共享記憶體操作的併發執行緒中,如果有一個改變資料,又沒有同步機制的條件下,就會產生競爭條件,可能會導致執行無效程式碼、bug等異常行為。

      競爭條件最簡單的解決方法是使用鎖。鎖的操作非常簡單,當一個執行緒需要訪問部分共享記憶體時,它必須先獲得鎖才能訪問。此執行緒對這部分共享資源使用完成之後,釋放鎖,然後其他執行緒才可再次獲得鎖並訪問這部分資源。

      然而,在實際使用中,這個方法經常導致死鎖現象。當不同執行緒要求得到同一個鎖時,死鎖就會發生,此時程式不會繼續執行,因為他們互相拿著對方需要的鎖。

      

      造成死鎖的原因:執行緒A在使用資源2,執行緒B在使用資源1,如果在沒有釋放鎖時,執行緒A又需要資源1,執行緒B又需要資源2,但是兩個資源的鎖都是被佔用的,而且在對方的鎖釋放之前都處於等待且不釋放鎖的狀態,此時就會造成死鎖。

      使用鎖來解決同步問題是一個可行的方式,但是也存在潛在的問題。

2、使用Lock進行執行緒同步

      示例:

# coding : utf-8

import threading

shared_resource_with_lock = 0
shared_resource_with_no_lock = 0
count = 100000

shared_resource_lock = threading.Lock()

# has lock
def increment_with_lock():
    global shared_resource_with_lock
    for i in
range(count): shared_resource_lock.acquire() shared_resource_with_lock += 1 shared_resource_lock.release() def decrement_with_lock(): global shared_resource_with_lock for i in range(count): shared_resource_lock.acquire() shared_resource_with_lock -= 1 shared_resource_lock.release()
# has no lock def increment_without_lock(): global shared_resource_with_no_lock for i in range(count): shared_resource_with_no_lock += 1 def decrement_without_lock(): global shared_resource_with_no_lock for i in range(count): shared_resource_with_no_lock -= 1 if __name__ == "__main__": t1 = threading.Thread(target=increment_with_lock) t2 = threading.Thread(target=decrement_with_lock) t3 = threading.Thread(target=increment_without_lock) t4 = threading.Thread(target=decrement_without_lock) t1.start() t2.start() t3.start() t4.start() t1.join() t2.join() t3.join() t4.join() print("the value of shared with lock is %s" %shared_resource_with_lock) print("the value of shared with no lock is %s" % shared_resource_with_no_lock)

      執行結果:

      

      在有鎖的情況下,我們會得到掙錢的結果,而沒有鎖的情況下,往往會出現錯誤的結果。

3、鎖狀態

      鎖有兩種狀態:locked(被某一執行緒拿到)和unlocked(可用狀態)

      操作鎖的方式:acquire()和release()

      如果狀態是unlocked,可以呼叫acquire將狀態改為locked

      如果狀態是locked,acquire會被block直到另一個執行緒呼叫release釋放鎖

      如果狀態是unlocked,呼叫release會導致RuntimError異常

      如果狀態是locked,可以呼叫release將狀態改為unlocked