2011-08-23 101 views
27

我想C#創建線程安全的特性,我想確保我是正確的道路上 - 這裏是我做了什麼 -線程安全屬性

private readonly object AvgBuyPriceLocker = new object(); 
private double _AvgBuyPrice; 
private double AvgBuyPrice 
{ 
    get 
    { 
     lock (AvgBuyPriceLocker) 
     { 
      return _AvgBuyPrice; 
     } 
    } 
    set 
    { 
     lock (AvgBuyPriceLocker) 
     { 
      _AvgBuyPrice = value; 
     } 
    } 
} 

閱讀此公告,它似乎好象這是沒有這樣做的正確的方式 -

C# thread safety with get/set

然而,這篇文章似乎並非如此,

http://www.codeproject.com/KB/cs/Synchronized.aspx

有沒有人有更明確的答案?

編輯:

,我想要做的getter/setter此屬性的原因爲B/C其實我希望它火的時候,它設置一個事件 - 這樣的代碼實際上是這樣的 -

public class PLTracker 
{ 

    public PLEvents Events; 

    private readonly object AvgBuyPriceLocker = new object(); 
    private double _AvgBuyPrice; 
    private double AvgBuyPrice 
    { 
     get 
     { 
      lock (AvgBuyPriceLocker) 
      { 
       return _AvgBuyPrice; 
      } 
     } 
     set 
     { 
      lock (AvgBuyPriceLocker) 
      { 
       Events.AvgBuyPriceUpdate(value); 
       _AvgBuyPrice = value; 
      } 
     } 
    } 
} 

public class PLEvents 
{ 
    public delegate void PLUpdateHandler(double Update); 
    public event PLUpdateHandler AvgBuyPriceUpdateListener; 

    public void AvgBuyPriceUpdate(double AvgBuyPrice) 
    { 
     lock (this) 
     { 
      try 
      { 
       if (AvgBuyPriceUpdateListener!= null) 
       { 
        AvgBuyPriceUpdateListener(AvgBuyPrice); 
       } 
       else 
       { 
        throw new Exception("AvgBuyPriceUpdateListener is null"); 
       } 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex.Message); 
      } 
     } 
    } 
} 

我非常新的做我的代碼線程安全的,所以請隨時告訴我,如果我在完全錯誤的方式去一下吧!

請問

+1

沒關係。我不明白,爲什麼你認爲它不是。鏈接的答案並不表示這樣做是一個壞主意。 –

+0

var property = new ConcurrentValue (); property.ReadValue(x => { //使用此線程安全值x }); property.WriteValue(()=> //複雜耗時的阻塞計算 return「Computed」; }); } 您可以創建一個ConcurrentValue類,用作具有鎖定功能的屬性值持有者,以獨佔方式編寫並執行多次讀取 - Object.Property.Write((=)=>耗時計算)//阻止Object.Property.Read (x =>) –

回答

18

既然你有一個原始值這一鎖定將正常工作 - 在其他問題的問題是,該屬性值是一個較爲複雜的類(一個可變的引用類型) - 鎖定將保護訪問和檢索你的類所持有的double值的實例。

如果你的屬性值是一個可變引用類型另一方面,鎖定不會防止一旦使用它的方法檢索到的類實例改變,這是另一個海報希望它執行的操作。

6

閱讀和雙打的寫作是原子反正( source閱讀和雙打的寫作不是原子,所以它會使用鎖來保護訪問雙是必要的,但是,對於許多類型的閱讀和寫作是原子彈,所以下面會同樣安全:

private float AvgBuyPrice 
{ 
    get; 
    set; 
} 

我的觀點是,線程安全性不是簡單地保護您的每一個屬性更復雜。舉個簡單的例子,假設我有兩個屬性AvgBuyPriceStringAvgBuyPrice

private string StringAvgBuyPrice { get; set; } 
private float AvgBuyPrice { get; set; } 

而且想我正是如此更新的平均買入價:

this.AvgBuyPrice = value; 
this.StringAvgBuyPrice = value.ToString(); 

這顯然不是線程安全的,並分別在保護性上述方式根本無濟於事。在這種情況下,應該以不同的級別執行鎖定,而不是按每個屬性級別執行鎖定。

+4

根據你的MSDN源文件,雙精度數*不保證是原子的:「其他類型的讀和寫,包括long,ulong,double和decimal,以及用戶定義的類型,都不是保證是原子的。「 – JeremyDWill

+0

@JeremyDWill好了 - 我編輯了我的問題,但是我認爲我的觀點依然存在。 – Justin

+0

我同意JeremyDWill,你應該考慮double是一個64位數字,所以當使用32位機器讀寫操作可以在多個步驟中完成。 –

16

鎖,你寫他們是毫無意義的。例如,讀取變量的線程將:

  1. 獲取鎖。
  2. 閱讀價值。
  3. 解鎖。
  4. 以某種方式使用讀取值。

沒有什麼可以從第3步如.NET變量訪問完成後,修改值停止另一個線程是原子(見下文警告),鎖沒有真正實現多在這裏:僅僅增加的開銷。與解鎖示例對比:

  1. 讀取值。
  2. 以某種方式使用讀取值。

另一個線程可能會改變步驟1和2之間的值,這與鎖定示例沒有什麼不同。

如果你想確保狀態時,你正在做一些處理不改變,你必須閱讀的價值並使用鎖的CONTEX內的值做處理:

  1. 獲取鎖。
  2. 閱讀價值。
  3. 以某種方式使用讀取值。
  4. 解鎖。

話雖如此,有些情況下當訪問變量時需要鎖定。這些通常是由於底層處理器的原因:例如,double變量不能在32位機器上作爲單個指令讀取或寫入,因此您必須鎖定(或使用替代策略)以確保損壞的值不是讀。

10

線程安全不是應該添加到變量中的東西,而應該添加到「邏輯」中。如果你爲所有的變量添加鎖,你的代碼仍然不一定是線程安全的,但它會像地獄一樣慢。 要編寫一個線程安全的程序,請查看您的代碼並確定多個線程可以使用相同的數據/對象的位置。爲所有這些關鍵地方添加鎖定或其他安全措施。

例如,假設僞碼以下位:

void updateAvgBuyPrice() 
{ 
    float oldPrice = AvgBuyPrice; 
    float newPrice = oldPrice + <Some other logic here> 
    //Some more new price calculation here 
    AvgBuyPrice = newPrice; 
} 

如果這個代碼是從多個線程同時調用,您鎖定邏輯已經沒有用了。設想線程A獲得AvgBuyPrice並進行一些計算。在完成之前,線程B也獲取AvgBuyPrice並開始計算。線程A在此期間完成並將新的值分配給AvgBuyPrice。然而,不久之後,它將被線程B覆蓋(仍然使用舊值),並且線程A的工作完全丟失。

那麼你如何解決這個問題呢?如果我們要使用鎖(這是最醜陋的和最慢的解決方案,但最簡單的,如果你只是用多線程開始),我們需要把所有這些改變AvgBuyPrice在鎖的邏輯:

void updateAvgBuyPrice() 
{ 
    lock(AvgBuyPriceLocker) 
    { 
     float oldPrice = AvgBuyPrice; 
     float newPrice = oldPrice + <Some other code here> 
     //Some more new price calculation here 
     AvgBuyPrice = newPrice; 
    } 
} 

現在,如果線程B想要在線程A仍然忙的時候進行計算,它將等待線程A完成,然後使用新值執行其工作。請記住,任何其他修改AvgBuyPrice的代碼也應該在它正在工作時鎖定AvgBuyPriceLocker!

但是,如果經常使用,這將會很慢。鎖非常昂貴,並且有很多其他機制可以避免鎖定,只需搜索無鎖算法即可。

+3

爲了增加Mart的評論,Joseph Albahari撰寫了一本關於所有線程方面的[優秀在線書籍](http://www.albahari.com/threading/)。該書以HTML或PDF格式提供。 –