2008-10-05 22 views
1

在多線程代碼中,當一個實例可能被多個線程讀取或寫入時,需要鎖定它們以安全地執行這些操作。在MTA中簡化鎖定

爲了避免重複創建一個對象來通過代碼鎖定和寫入一堆鎖語句,我創建了一個通用類來處理鎖定。

我是否缺少任何東西,在概念上?這應該工作,對吧?

public class Locked<T> where T : new() 
{ 
    private readonly object locker = new object(); 

    private T value; 

    public Locked() 
     : this(default(T)) 
    { } 

    public Locked(T value) 
    { 
     this.value = value; 
    } 

    public T Get() 
    { 
     lock (this.locker) 
     { 
      return this.value; 
     } 
    } 

    public void Set(T value) 
    { 
     lock (this.locker) 
     { 
      this.value = value; 
     } 
    }  
} 

而且它的一個例子在一個類中使用:

private Locked<bool> stopWorkerThread = new Locked<bool>(); 

public void WorkerThreadEntryPoint() 
{ 
    while (true) 
    { 
     if (this.stopWorkerThread.Get()) 
     { 
      break; 

} 

此外,我將如何測試這樣的事情,以自動的方式(例如,創建單元測試)?

最後,我能做些什麼來實現++和 - 運營商,以避免這種情況:

 this.runningThreads.Set(this.runningThreads.Get() + 1); 

回答

3

這只是鎖了get/set的持續時間;當然,在許多常見情況下,無論如何,這只是原子的原因,只是由於數據的大小。

但是,實際上大多數鎖都需要超出這個範圍,就像收集鎖只是添加等的幫助一樣 - 呼叫者通常需要一個鎖來跨越「是嗎?如果是這樣更新,否則添加「序列。

對於像bool這樣簡單的東西來說,「volatile」可能會更簡單地解決問題 - 特別是如果它只是用於循環退出。您可能還想考慮[MethodImpl(MethodImplOptions.Synchronized)] - 雖然我個人更喜歡私人鎖定對象(就像您使用過的),以防止外部人員鎖定對象時出現問題(上述使用「this」作爲鎖)。對於單元測試這個,你需要一些東西來證明它首先被破壞 - 這是很難的,因爲操作太小(並且對於大多數數據類型已經是原子的)。它避免的其他事情之一(即易失性也可以修復)緩存在寄存器中,但這又是一次優化,很難證明它已被破壞。

如果您對鎖封裝器感興趣,可以考慮現有的代碼,如this

1

上面的代碼有很多潛在的和真正的多線程問題,我不會在真實世界中使用類似的東西。例如:

this.runningThreads.Set(this.runningThreads.Get() + 1); 

這裏有一個很明顯的競爭條件。當Get()調用返回時,該對象不再被鎖定。要做一個真正的職位或預先增量,計數器將需要從Get之前鎖定到Set之後。

如果您所做的只是同步讀取,您並不總是需要完全鎖定。

一個更好的鎖定接口(我認爲)要求你明確地鎖定你需要的實例。我的經驗是主要用C++,所以我不能推薦一個完整的實現,但是我的首選語法可能是這個樣子:

using (Locked<T> lock = Locked<T>(instance)) 
{ 
    // write value 
    instance++; 
} 

// read value 
print instance; 
+0

示例代碼不會,除非鎖定(實例)的工作被宣佈其他地方重新用於所有呼叫者 - 否則每個呼叫者都有不同的鎖定,這會破壞目的。然後它會更簡單地聲明一個對象並鎖定(obj) – 2008-10-05 08:29:38