我對使用它的編程專案的多執行緒進行了研究(這里是第一次......)。如果您認為我的以下陳述是正確的,或者更確切地說,對錯誤或需要更正的陳述發表評論,我將不勝感激。
- 鎖是一個可以通過參考傳遞給函式、方法等的物件。然后(在本例中)函式可以使用該鎖定物件參考來安全地對資料(本例中為變數)進行操作。它通過獲取鎖、修改變數然后釋放鎖來實作這一點。
- 可以創建一個執行緒來定位一個函式,該函式可以獲得對鎖的參考(然后實作上述內容)。
- 鎖不保護特定的變數、物件等。
- 除非獲得(和釋放)鎖,否則鎖不會保護或做任何事情。
- 因此,程式員有責任使用鎖來實作所需的保護。
- 如果在執行緒 A 執行的函式內獲得鎖,這不會立即影響任何其他正在運行的執行緒 B。即使執行緒 A 和 B 的目標函式參考同一個鎖物件也不會。
- 僅當執行緒 B 的目標函式想要獲取相同的鎖(即通過相同的參考鎖物件)時,此時執行緒 A 的目標函式已經獲得了該鎖,該鎖傳達了對該執行緒 B 中的兩個執行緒的影響將暫停進一步執行,直到執行緒 A 的目標函式再次釋放鎖。
- 因此,鎖定的鎖只會暫停執行緒的執行,如果它的目標函式想要(并等待)自己獲取相同的鎖。因此,執行緒A獲得鎖,只能阻止執行緒B獲得相同的鎖,僅此而已。
如果我想在設定變數時使用鎖來防止競爭條件,我(作為程式員)需要:
- 將鎖傳遞給想要設定變數的執行緒所針對的所有函式,并且
- 在我設定變數(然后釋放它)之前,每次都在每個函式中獲取鎖。(*)
- 如果我只創建一個針對函式的執行緒而不提供對鎖定物件的參考并讓它設定變數或
- 如果我通過其目標函式具有鎖定物件的執行緒設定變數,但在操作之前沒有獲取它,我將無法實作變數的執行緒安全設定。
(*)只要變數不能被其他執行緒訪問,就應該獲取鎖。現在,我喜歡將其與資料庫事務進行比較......我鎖定資料庫(?獲取鎖)直到我的一組指令完成,然后我提交(?釋放鎖)。
示例如果我想創建一個類,其成員_value應該以執行緒安全的方式設定,我將實作以下兩個版本之一:
class Version1:
def __init__(self):
self._value:int = 0
self._lock:threading.Lock = threading.Lock()
def getValue(self) -> int:
"""Getting won't be protected in this example."""
return self._value
def setValue(self, val:int) -> None:
"""This will be made thread-safe by member lock."""
with self._lock:
self._value = val
v1 = Version1()
t1_1 = threading.Thread(target=v1.setValue, args=(1)).start()
t1_2 = threading.Thread(target=v1.setValue, args=(2)).start()
class Version2:
def __init__(self):
self._value:int = 0
def getValue(self) -> int:
"""Getting won't be protected in this example."""
return self._value
def setValue(self, val:int, lock:threading.Lock) -> None:
"""This will be made thread-safe by injected lock."""
with self._lock:
self._value = val
v2 = Version2()
l = threading.Lock()
t2_1 = threading.Thread(target=v2.setValue, args=(1, l)).start()
t2_2 = threading.Thread(target=v2.setValue, args=(2, l)).start()
- 在
Version1中,我作為類提供者,可以保證設定_value始終是執行緒安全的... - ...因為在 中
Version2,我的類的用戶可能會將不同的鎖物件傳遞給兩個生成的執行緒,從而使鎖保護變得無用。 - 如果我想讓我的類的用戶自由地將設定包含
_value到應該以執行緒安全方式執行的更大的步驟集合中,我可以將Lock參考注入到Version1'__init__函式中并將其分配給_lock成員。因此,將保證類的執行緒安全操作,同時仍允許該類的用戶為此目的使用“她自己的”鎖。
0-15 分現在將評估我對鎖的理解程度(錯誤)... :-D
uj5u.com熱心網友回復:
- 將全域變數用于鎖也很常見。這取決于鎖在保護什么。
- 沒錯,雖然有些無意義。任何函式都可以使用鎖,而不僅僅是執行緒目標的函式。
- 如果您的意思是鎖與其保護的資料之間沒有直接聯系,那是真的。但是您可以定義一個包含需要保護的值和對其鎖的參考的資料結構。
- 真的。雖然就像我在 3 中所說的,你可以定義一個資料結構來封裝資料和鎖。您可以將其設為一個類,并讓類方法根據需要自動獲取鎖。
- 正確的。但請參閱 4 了解如何自動執行此操作。
- 正確的。
- 正確的。
- 正確的。
- 如果它不是全域鎖,則更正。
- 部分正確。如果您只是讀取變數,您還應該經常獲取鎖。如果讀取物件不是原子的(例如,它是一個串列并且您正在讀取多個元素,或者您讀取相同的標量物件變數次并期望它是穩定的),您需要防止另一個執行緒在您修改它時修改它閱讀。
- 正確的。
- 正確的。
- 正確的。這是我在上面 3 和 4 中描述的一個例子。
- 正確的。這就是為什么 13 中的設計通常更好的原因。
- 這很棘手,因為鎖定的粒度需要反映所有需要保護的物件。你的類只保護那個變數的賦值——它會在與呼叫者提供的鎖相關的所有其他步驟完成之前釋放鎖。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/481887.html
標籤:Python 多线程 python-多线程 竞争条件
