2013-11-25 116 views
4

我有一個C#類與靜態成員,這是從多個線程讀取並寫入一個線程。C#線程安全靜態成員

據我所知,Uint64讀寫不是所有系統上的原子操作,所以我必須手動保證線程安全。

我對如何做到這一點有一些想法。

  1. 做它和原子包裝類,就像在c + +中的std :: atomic。在C#中有類似的東西嗎?

  2. 在靜態字段中使用volatile修飾符。然而這是不允許的。爲什麼?

  3. 我終於做到以下幾點:

    private static object tick_time_lock; 
    private static UInt64 _currentTickTime; 
    public static UInt64 CurrentTickTime 
    { 
        get 
        { 
         return _currentTickTime; 
        } 
        set 
        { 
         lock (tick_time_lock) 
         { 
          _currentTickTime = value; 
         } 
        } 
    } 
    

這是使這個領域的線程安全的正確方法?

+0

是的,但是請記住,當您有很多線程設置時,它會放慢速度。也會有競賽狀況。例如,如果將值更新爲... 1,則下一個線程可能會將其更新爲2,但之後第4個可能會將其更新爲... 4,並且第3個可能由於某種競爭條件而晚點進來並覆蓋最後更新的價值。順便說一句,你需要新增tick_time_lock對象。 –

+0

您可能正在尋找['Interlocked'](http://msdn.microsoft.com/zh-cn/library/vstudio/system.threading.interlocked(v = vs.110).aspx)實用程序類。 – millimoose

回答

5

這是使這個領域的線程安全的正確方法?

監視器鎖是無意義的,除非給定資源的所有訪問被同步。除非您還鎖定get訪問器,否則鎖定set訪問器是相當無用的。正如你所說,讀取和寫入UInt64值在所有平臺上都不是原子的。在set訪問器中只有第一個字被寫入時,如果在get訪問器中讀取該字段會發生什麼情況?你會得到一個撕裂的閱讀。

在靜態字段中使用volatile修飾符。然而這是不允許的。爲什麼?

C#語言設計師認爲確保所有volatile字段訪問都是原子性的是有益的。作爲折衷,您不能將任何64位字段聲明爲volatile。我不知道爲什麼做出這個決定。也許他們希望避免在某些易失性讀/寫操作中增加「隱藏」開銷,而要求開發人員依賴像Thread.Volatile[Read/Write](ref long)這樣的框架級設施來處理64位值。

它和原子包裝類一樣,就像C++中的std :: atomic。在C#中有類似的東西嗎?

是。存在通過System.Threading.Interlocked類暴露的框架級原子操作,包括Read,Exchange,CompareExchange

1

我想你需要實例化你的鎖對象。另外,也可以使用鎖get

private static Object tick_time_lock = new Object(); 
private static UInt64 _currentTickTime; 
public static UInt64 CurrentTickTime 
{ 
    get 
    { 
     lock (tick_time_lock) 
     { 
      return _currentTickTime; 
     } 
    } 
    set 
    { 
     lock (tick_time_lock) 
     { 
      _currentTickTime = value; 
     } 
    } 
} 
4

看看Interlocked類,它爲由多個線程共享的變量提供了原子操作。

1

不,你這樣做是完全錯誤的。鎖定只讀/寫操作不會幫助你。例如,該代碼不是線程安全的,即使get和set可能被鎖把守:

var time = Clock.CurrentTickTime; 
time += 1 
Clock.CurrentTickTime = time 

你會需要把鎖定所有解決此代碼,使其線程安全的。另外,使系統的所有組件都是線程安全的並不能保證整個系統是線程安全的。它實際上會增加死鎖的可能性並使調試更加困難。

簡單地說,你正在從完全錯誤的角度接近線程安全。首先,忘記同步全局狀態。具有全局狀態和線程安全性非常困難。相反,讓所有狀態都是線程本地的,並且只能在精確定義的點上同步這個狀態,在那裏你可以保證線程安全。