2013-02-20 98 views
0

我有一個執行類似這樣的操作的後臺線程:成員集合迭代線程安全

class Client 
{ 
    public ReadOnlyCollection<IPAddress> Servers { get; private set; } 

    void UpdateServers(List<IPAddress> servers) 
    { 
     // this will be called on a background thread 

     this.Servers = new ReadOnlyCollection(servers); 
    } 
} 

和主線程,消費者可能要遍歷Servers財產。

我知道用foreach,迭代將是線程安全的,因爲如果在迭代期間調用UpdateServers,則迭代器將屬於舊實例。

隨着for循環,迭代可製成與安全:

var serverList = client.Servers; 
for (int x = 0 ; x < serverList.Count ; ++x) 
{ 
    DoSomething(serverList[ x ]); 
    // ... 
} 

但我不知道是如果有任何的方式來保證編譯器將生成(或迫使其產生,如果如果消費者決定迭代:

for (int x = 0 ; x < client.Servers.Count ; ++x) 
+1

我建議不要訪問服務器變量,而是有一個接收閉包並執行迭代本身的方法 - 或者有一個服務器的適當getter,它的名字清楚地表明它應該被調用和存儲變成一個變量,而不是反覆使用。 – entonio 2013-02-20 00:16:24

回答

2

編譯器會生成您告訴它的代碼。可能會進行一些優化以避免一次又一次加載字段的值,但實際上不應該依賴這些字段。

因此,編譯器可能會以這種方式生成代碼,但它並不是必須的,所以您應該編寫代碼,就好像它沒有。

此外,這樣的事情可能非常棘手(例如,我認爲你也應該使字段volatile)。最好的選擇通常是使用鎖定,除非那些會給你帶來真正的性能問題(這種情況很少發生)。

+0

我們生活在對不存在績效擔憂的恐懼之中。這些問題很難。演出前的正確性。 +1 – spender 2013-02-20 00:29:27

1

如果你不介意他們互相鎖定,這可能是可行的。儘管如此,它在內存使用和對象創建方面會有點沉重。有可能是一個更清潔的方式。

private ReadOnlyCollection<IPAddress> _servers; 
public ReadOnlyCollection<IPAddress> Servers { 
    get { 
     lock(_servers) { 
      return new ReadOnlyCollection<IPAddress>(_servers); 
     } 
    } 

    private set { 
     lock(_servers) { 
      _servers = value; 
     } 
    } 
} 
+0

沒有理由創建包含另一個「ReadOnlyCollection」的'ReadOnlyCollection',它只是增加開銷。 – svick 2013-02-20 00:20:21

+0

我想的很多。繼續編輯並優化它。我對那個特定對象的認識是相當有限的,並且在一個鎖定內返回似乎是荒謬的。 這裏的目標是在訪問時爲對象提供訪問時的對象,而不是在迭代時保持對它的鎖定。它似乎是一個內存vs鎖定性能問題。 – Syddraf 2013-02-20 00:38:06

0

你應該使用鎖如上的人建議,但你也可以最大限度地減少鎖定正在舉行不迭代到列表的引用,並使用.ToArray代替然後再遍歷該時間量。

+0

在Syddraf的回答中,持鎖時沒有迭代。 – svick 2013-02-20 00:19:41

+0

同意但將該方法與for循環相結合,因爲op狀態有 – 2013-02-20 00:20:13

+0

我擔心的是,消費者可能不會遵循我們的文檔。我一直在尋找一種隱含線程安全的設計,而不需要消費者通過循環來確保迭代是安全的。 – 2013-02-20 00:24:38