2012-01-03 55 views
3

我在想,如果這個語句會導致同步問題:線程安全的訪問<T>性能

List<Character> characters = World.CharacterManager.Characters; 

「人物」是一類

「CharacterManager.Characters」會是這個樣子:

public List<Character> Characters 
{ 
    get 
    { 
     lock (this.objLock) { return this.characters; } 
    } 
} 

會造成同步問題嗎?

我想使用引用列表遍歷來找到我正在尋找的字符。

+1

我們需要更多的上下文,你需要鎖定嗎?你使用多線程?你擁有的鎖並不能真正實現任何目標。 – Gary 2012-01-03 22:39:30

+3

我認爲可以安全地假設多線程,因爲他不會在單線程上下文中詢問同步... – Phil 2012-01-03 22:50:56

+0

謝謝菲爾。是的,我可以確認這種情況適用於多線程應用程序。 – TheAJ 2012-01-03 23:30:03

回答

2

鎖是無用的方式。你將不得不使用一個線程安全的集合,例如威爾建議,或者如果你不需要寫訪問,你可以只露出一個只讀版本的名單中,像這樣:

public ReadOnlyCollection<Character> Characters { 
    get { 
    lock (locker) { return this.characters.AsReadOnly(); } 
    } 
} 

這些藏品不能修改,所以如果你的Character類型是不可變的,你就沒有任何同步問題。如果Character是可變的,那麼您又遇到了一個問題,但即使使用線程安全的集合,您也會遇到該問題。我希望你知道這一點。您還可以公開返回IList<Character>的屬性,但通常我會發現告訴調用方該對象只讀是最好的。

如果您需要寫入權限,您也可以通過在CharacterManager的範圍內提供適當的方法並對其進行同步來實現。傑西寫了一個很好的例子來說明如何做到這一點。

編輯:Syncoloot不存在ICollection <T>。

+0

請注意,列表僅實現'SyncRoot'顯式,所以它必須轉換爲'ICollection'才能被訪問。 (ref:http://stackoverflow.com/a/4067371/3312) – 2012-01-03 23:13:54

+0

謝謝!我也搜索了一下,發現他們[停止了這種模式](http://blogs.msdn.com/b/bclteam/archive/2005/03/15/396399.aspx)(很久以前)。我編輯了答案。 – Andreas 2012-01-03 23:16:54

+0

謝謝你。如果我打電話像這樣:World.CharacterManager.Characters.Count 它仍然是一個無用的鎖? – TheAJ 2012-01-03 23:36:37

9

問題是,您在get期間鎖定,但是一旦每個線程都有對集合的引用,它們都可以同時對其執行操作。由於List<T>的成員不是線程安全的,因此在迭代,添加,刪除集合等時會遇到隨機錯誤和異常。

您可能需要返回一個線程安全的集合。沒有100%兼容的線程安全版本,因此您需要查看System.Collections.Concurrent並找到可以使用的版本。

0

如果您只想讓調用者能夠枚舉列表,那麼您的屬性應該具有IEnumerable類型。如果是這種情況,那麼我也會製作一份該清單的副本並返回副本。如果在枚舉列表時發生了更改,那麼它將變爲無效並拋出異常。折衷是調用者可能沒有列表的最新版本。我傾向於將其轉換爲名爲GetCharactersAsOfNow()的方法,而不是一個屬性來幫助顯示調用方需要爲每個調用獲取更新列表。

但是,如果您打算允許調用者修改列表,則必須注意列表不是線程安全的,並且需要調用者執行線程同步。鑑於調用者現在有這個責任,那麼你不再需要你的財產獲取者的鎖。

1

調用代碼實際上是否需要能夠添加和從列表中刪除?如果是這樣,那就是not considered a best practice。這裏有一個(可能)沒有辦法這一要求實現,而不是把添加和刪除的Character物品進入CharacterManager類本身:

internal sealed class CharacterManager 
{ 
    private readonly IList<Character> characters = new List<Character>(); 

    public ReadOnlyCollection<Character> Characters 
    { 
     get 
     { 
      lock (this.characters) 
      { 
       return this.characters.AsReadOnly(); 
      } 
     } 
    } 

    public void Add(Character character) 
    { 
     lock (this.characters) 
     { 
      this.characters.Add(character); 
     } 
    } 

    public void Remove(Character character) 
    { 
     lock (this.characters) 
     { 
      this.characters.Remove(character); 
     } 
    } 
} 
+0

你打敗了我幾秒鐘:-) – Andreas 2012-01-03 23:06:22

+0

你得到一個+1提到底層'Character'類的可變性問題。 – 2012-01-03 23:07:36