避免 thread 競速(Race Condition)是多執行緒編程中常見的挑戰之一。
Race Condition 發生在多個執行緒同時訪問
和修改共享資源
時,因為執行緒之間的執行順序無法預測,可能會導致數據的不一致性或意外行為。
本文主要介紹如何使用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執行緒在互跑時,互相覆蓋了對方的結果。
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 = threading.Lock()
創建了一個鎖(Lock),以確保同一時間內只有一個執行緒可以進入 with lock:
區塊來操作 shared_counter
。with lock:
區塊時,其他執行緒會被阻塞在該區塊外,直到鎖被釋放。Lock
後,確保了 shared_counter
的讀取、遞增和寫回操作,這樣可以避免多個執行緒同時修改 shared_counter
,從而避免 Race Condition 的發生。