2012-07-19 116 views
4

reading經過約LazyInitializer,它是:C#懶惰初始化&&比賽初始化?

它提供了具有多線程 比賽初始化初始化的另一種方式。

這裏是一個示例:

Expensive _expensive; 
public Expensive Expensive 
    { 
     get // Implement double-checked locking 
     { 
      LazyInitializer.EnsureInitialized (ref _expensive,() => new Expensive()); 
      return _expensive; 
     } 
    } 

問題#1

看: enter image description here

爲什麼#A說,它的工具雙重檢查鎖定?它只是一個獲得成功?

問題2

不會#B(lambda表達式)是線程安全的?

問題#3

於是我去搜索這個通過看樣品「比賽到初始化」件事:

volatile Expensive _expensive; 
public Expensive Expensive 
{ 
    get 
    { 
    if (_expensive == null) 
    { 
     var instance = new Expensive(); 
     Interlocked.CompareExchange (ref _expensive, instance, null); 
    } 
    return _expensive; 
    } 
} 

,然後我想到了:心不是比賽初始化是線程安全的?

E/G/2個,如果線程進入:

enter image description here

昂貴的對象將被創建兩次!

如此反覆,3問題

1)爲什麼說#A的工具雙重檢查鎖定?它只是一個獲得成功?

2)#B(l​​ambda表達式)是線程安全的嗎?

3)心不是比賽來初始化是線程安全的

+1

評論「// Implement ...」聽起來更像是一個未來計劃,我會說 – Qnan 2012-07-19 07:23:55

回答

5

還有的EnsureInitialized各種重載。有些接受synclock對象(可以是null並將由EnsureInitialized方法創建)。其他人沒有synclock作爲參數。所有的EnsureInitialized保證,如果在未初始化對象的同時由兩個(或更多)不同的線程調用,則兩個線程將接收對同一對象的引用。所以:

Expensive _expensive; 

// On thread 1 
LazyInitializer.EnsureInitialized (ref _expensive,() => new Expensive()); 

// On thread 2 
LazyInitializer.EnsureInitialized (ref _expensive,() => new Expensive()); 

這個_expensive會被兩個線程看到的對象是一樣的。

唯一的問題是new Expensive()可能會被調用兩次(每個線程一次,所以在多線程競爭中它可能會被調用更多次。)

如果你不想要它,使用synclock過載:

Expensive _expensive; 
object _sync = null; 
bool _useless; 

// On thread 1 
LazyInitializer.EnsureInitialized (ref _expensive, ref useless, ref _sync,() => new Expensive()); 

// On thread 2 
LazyInitializer.EnsureInitialized (ref _expensive, ref useless, ref _sync,() => new Expensive()); 

現在new Expensive()將只調用一次,用於運行兩個(或多個)線程的每一個可能的組合。

+0

確實_race-to-initialize_是_race condition_嗎? (這是相同的術語?) – 2012-07-19 08:03:38

+0

@RoyiNamir它取決於如果初始化有副作用:-)如果它沒有副作用,那麼它不是一個真正的種族,因爲(取自維基)'A競爭狀況或種族危害是電子系統或過程中的缺陷,因此過程的輸出或結果出乎意料地並且嚴格依賴於其他事件的順序或時間。「無副作用=>輸出沒有差異(顯然非常資源的高度利用可以被歸類爲副作用)。如果方法是'新列表()',那麼沒有任何問題。如果它是「將100美元轉讓給某人」,那麼就是。 – xanatos 2012-07-19 08:06:36

+0

那麼我使用'LazyInitializer.EnsureInitialized'(沒有syncobj)有什麼好處?我可以使用'懶惰 _expensive = new懶惰 (()=> new Expensive(),true);'這也是線程sagfe &&懶惰... – 2012-07-19 08:16:29