2014-12-03 36 views
0

我有这段代码,我试图在并发字典中为一个键(键一直都是)更新项目(这是一个列表)。在更新ConcurrentDictionary中的元素时发生同步问题

这里是我的代码 - :

class Program 
    { 
     private static readonly ConcurrentDictionary<string, List<int>> s_mapDatatypeToSource = new ConcurrentDictionary<string, List<int>>(); 
     static void Main(string[] args) 
     { 


       try 
       { 
        Parallel.For(0, 10000000, i => AddItemToList(i, "local")); 
       } 
       catch (Exception exception) 
       { 
        Console.WriteLine(string.Format("Exception thrown ...{0}", exception.Message)); 
        Console.ReadLine(); 
        throw; 
       } 

      Console.WriteLine("Completed without exception"); 
      Console.ReadLine(); 

     } 

     private static void AddItemToList(int dataKey, string sourceName) 
     { 
      s_mapDatatypeToSource.AddOrUpdate(sourceName.ToUpperInvariant(), new List<int> { dataKey }, (s, list) => 
      { 

       { 
        list.Add(dataKey); 
        return list; 
       } 


      }); 

     } 
    } 

一个出上面的代码10次抛出异常 - “源阵列不够长检查SRCINDEX和长度,以及数组的下限。”

我知道这是一个同步问题的列表,但我不明白为什么会这样,因为ConcurrentDictionary是线程安全的。所以我认为,它可以让只有一个线程在同一时间来更新我的名单,所以不应该有任何problem-:

我知道我失去了一些东西 - 建议,请 - :

+0

请以及格式化你的代码不可读 – 2014-12-03 06:40:49

+0

GetOrAdd和AddOrUpdate不是原子(线程安全)的MSDN自己说: 此外,虽然ConcurrentDictionary的所有方法是线程安全的,并不是所有的方法都是原子的,特别是GetOrAdd和AddOrUpdate。传递给这些方法的用户委托在字典的内部锁之外被调用。 (这是为了防止未知代码阻塞所有线程。) – user3359453 2016-10-06 02:31:31

回答

4

ConcurrentDictionary可以thread-安全,但列表不是。

看反编译的方法从表:

public void Add(T item) 
{ 
    if (this._size == this._items.Length) 
    this.EnsureCapacity(this._size + 1); 
    this._items[this._size++] = item; 
    ++this._version; 
} 

线程#1和#THEAD 2可以在同一时间通过if (this._size == this._items.Length)。线程#1将在这里设置值this._items[this._size++] = item;,但线程#2将导致IndexOutOfRangeException。您需要线程安全列表。

更新可以使用SynchronizedCollection而不是List