Lock 和 Rlock 物件的區別


併發程式設計涉及多個執行緒或程序同時執行,這可能導致競爭條件和資料不一致等問題。為了解決這些問題,Python 提供了同步原語,包括 Lock 和 RLock 物件。雖然這兩個物件都用於控制對共享資源的訪問,但它們的行為和用法有所不同。

Lock 物件是基本的互斥機制。它允許多個執行緒獲取和釋放鎖,但在任何給定時間只有一個執行緒可以持有鎖。當一個執行緒試圖獲取另一個執行緒已經持有的鎖時,它將被阻塞,直到鎖可用。這確保了受鎖保護的關鍵程式碼段一次只由一個執行緒執行。

另一方面,RLock 物件透過引入重入性或遞迴鎖來擴充套件 Lock 的功能。重入性允許已經持有鎖的執行緒再次獲取它而不會導致死鎖。這在巢狀函式呼叫或程式碼段需要多級鎖定的場景中特別有用。使用 RLock 物件,執行緒可以多次獲取鎖,並且必須釋放相同次數的鎖才能使它對其他執行緒可用。這確保了鎖保持持有狀態,直到所有相應的釋放操作都執行完畢。

Lock 物件

Lock 物件是 Python threading 模組中基本的互斥機制。它的主要目的是在併發環境中控制對共享資源的訪問,確保任何給定時間只有一個執行緒可以持有鎖。這保證了受鎖保護的關鍵程式碼段的獨佔執行。

Lock 物件的行為很簡單。多個執行緒可以嘗試獲取和釋放鎖,但只有一個執行緒會成功獲取它。如果一個執行緒嘗試獲取另一個執行緒已經持有的鎖,它將被阻塞並進入等待狀態,直到鎖可用。一旦獲取了鎖,執行緒就可以安全地進入關鍵程式碼段並對共享資源執行必要的操作。完成關鍵程式碼段後,鎖被釋放,允許其他執行緒獲取它。

Lock 物件提供兩種基本方法:acquire() 和 release()。acquire() 方法用於獲取鎖。如果鎖已被另一個執行緒持有,則呼叫執行緒將被阻塞並等待直到鎖被釋放。一旦獲取了鎖,執行緒就可以繼續執行關鍵程式碼段。完成關鍵程式碼段後,呼叫 release() 方法釋放鎖,使其可供其他執行緒獲取。

關於 Lock 物件需要注意的一點是,它們不支援重入性。重入性是指執行緒能夠多次獲取同一個鎖而不會導致死鎖的能力。對於 Lock,如果已經持有鎖的執行緒試圖再次獲取它,將導致死鎖,執行緒將無法繼續執行,導致程式掛起。因此,Lock 物件適用於不需要重入行為的場景,例如簡單的同步或沒有巢狀函式呼叫的情況。

RLock 物件

RLock 物件(代表“可重入鎖”)是 Lock 物件的擴充套件,它解決了非重入鎖的限制。它支援重入性,允許執行緒多次獲取鎖而不會導致死鎖。

RLock 物件的關鍵特性是它能夠處理遞迴鎖獲取。這意味著執行緒可以以巢狀方式多次獲取鎖。每次獲取都必須與等量的釋放匹配才能釋放鎖。這種行為在巢狀函式呼叫或程式碼段需要多級鎖定的場景中特別有用。

RLock 物件提供與 Lock 物件相同的 acquire() 和 release() 方法,使其易於使用。此外,它還引入了兩種附加方法:帶阻塞引數的 acquire() 和帶計數引數的 release()。

帶阻塞引數的 acquire() 方法允許對鎖獲取進行細粒度控制。透過設定 blocking=False,執行緒可以嘗試獲取鎖,但如果鎖已被另一個執行緒持有,則不會阻塞。這使執行緒能夠在鎖無法立即獲得時執行替代操作或執行不同的程式碼路徑。

帶計數引數的 release() 方法能夠釋放指定次數的鎖。這線上程需要逐步釋放巢狀鎖獲取或獲取和釋放次數可能動態變化的情況下非常有用。

示例

這是一個演示在 Python 中使用 Lock 和 RLock 物件的示例程式碼片段:

import threading

# Shared resource
shared_resource = 0

# Lock objects
lock = threading.Lock()
rlock = threading.RLock()

# Function using Lock
def increment_with_lock():
    global shared_resource
    lock.acquire()
    try:
        shared_resource += 1
    finally:
        lock.release()

# Function using RLock
def increment_with_rlock():
    global shared_resource
    rlock.acquire()
    try:
        shared_resource += 1
        rlock.acquire()  # Nested acquisition
        try:
            shared_resource += 1
        finally:
            rlock.release()  # Nested release
    finally:
        rlock.release()

# Create multiple threads to increment shared_resource
num_threads = 5

# Using Lock
threads_with_lock = []
for _ in range(num_threads):
    thread = threading.Thread(target=increment_with_lock)
    threads_with_lock.append(thread)
    thread.start()

for thread in threads_with_lock:
    thread.join()

print("Value of shared_resource using Lock:", shared_resource)

# Reset shared_resource
shared_resource = 0

# Using RLock
threads_with_rlock = []
for _ in range(num_threads):
    thread = threading.Thread(target=increment_with_rlock)
    threads_with_rlock.append(thread)
    thread.start()

for thread in threads_with_rlock:
    thread.join()

print("Value of shared_resource using RLock:", shared_resource)

在這個程式碼中,我們有一個共享資源 (shared_resource),它由多個執行緒遞增。increment_with_lock() 函式演示了使用 Lock 物件來確保對遞增共享資源的關鍵程式碼段的獨佔訪問。類似地,increment_with_rlock() 函式展示了 RLock 物件的使用,允許遞迴鎖獲取和釋放。

選擇 Lock 和 RLock

在決定在併發程式中使用 Lock 還是 RLock 時,請考慮以下指導原則:

  • 當您只需要基本的互斥機制並且不需要重入性時,使用 Lock。Lock 物件輕量級,適用於沒有巢狀函式呼叫或多級鎖定的簡單同步場景。

  • 當您有巢狀函式呼叫或需要多級鎖定的程式碼段時,使用 RLock。RLock 允許執行緒遞迴地獲取鎖,確保在這種場景下的正確同步。它支援重入行為,並在多次獲取鎖時防止死鎖。

結論

在 Python 併發程式設計中,Lock 和 RLock 物件作為同步原語來控制對共享資源的訪問。Lock 物件提供基本的互斥,而 RLock 物件透過提供重入支援來擴充套件其功能。瞭解這兩個物件之間的區別對於編寫健壯且執行緒安全的程式碼至關重要。

更新於:2023年8月14日

560 次瀏覽

啟動你的職業生涯

完成課程獲得認證

開始學習
廣告