2024-08-11|閱讀時間 ‧ 約 26 分鐘

[Python]使用Lock避免threading競速導致數據的不一致性的問題

避免 thread 競速(Race Condition)是多執行緒編程中常見的挑戰之一。

Race Condition 發生在多個執行緒同時訪問修改共享資源時,因為執行緒之間的執行順序無法預測,可能會導致數據的不一致性或意外行為。

本文主要介紹如何使用Lock來避免此狀況出現。


首先先看沒有Lock的範例。

來模擬Race Condition的現象,假設沒有這個現象,最後shared_counter數值應該是要10000,10個執行緒疊加的結果才對。

import threading
import time

# 定義一個共享資源
shared_counter = 0

def increment_counter():
global shared_counter
for _ in range(1000):
temp = shared_counter
time.sleep(0.00001) # 模擬執行緒切換
shared_counter = temp + 1

# 建立多個執行緒
threads = []
for _ in range(10):
thread = threading.Thread(target=increment_counter)
threads.append(thread)
thread.start()

# 等待所有執行緒完成
for thread in threads:
thread.join()

print(f"Final counter value: {shared_counter}")

但數值卻在1000左右亂跑,而且不固定數值,這情況就是Race Condition所造成的,A執行緒跟B執行緒在互跑時,互相覆蓋了對方的結果。

使用Lock的範例

import threading
import time

# 定義一個共享資源
shared_counter = 0

# 建立一個 Lock
lock = threading.Lock()

def increment_counter():
global shared_counter
for _ in range(1000):
# 使用 Lock 保護共享資源
with lock:
shared_counter += 1
time.sleep(0.00001) # 模擬執行緒切換

# 建立多個執行緒
threads = []
for _ in range(10):
thread = threading.Thread(target=increment_counter)
threads.append(thread)
thread.start()

# 等待所有執行緒完成
for thread in threads:
thread.join()

print(f"Final counter value: {shared_counter}")

不管執行了幾次,結果一樣都是我們預期的1000。

  • Lock 機制
    • lock = threading.Lock() 創建了一個鎖(Lock),以確保同一時間內只有一個執行緒可以進入 with lock: 區塊來操作 shared_counter
    • 當一個執行緒進入 with lock: 區塊時,其他執行緒會被阻塞在該區塊外,直到鎖被釋放。
  • 避免 Race Condition
    • 使用 Lock 後,確保了 shared_counter 的讀取、遞增和寫回操作,這樣可以避免多個執行緒同時修改 shared_counter,從而避免 Race Condition 的發生。



分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.