2014-03-26 47 views
-3

我身邊有一個ConcurrentList實施看,這才發現一個: http://deanchalk.com/2010/10/c-fast-parallel-concurrentlistt-implementation/ConcurrentList - 螺紋safetly沒有給出

的代碼工作正常,你可以找到在指定網頁的實現,但是當我改變了DoWork方法:

原:

static void DoWork(ICollection<int> list, int count) 
{ 
    for (var i = 0; i < count; i++) 
    { 
     list.Add(i); 
     // use spinwait to emulate work but avoiding 
     // context switching 
     Thread.SpinWait(100000); 
    } 
} 

更改時間:

static void DoWork(ICollection<int> list, int count) 
{ 
    for (var i = 0; i < count; i++) 
    { 
     var foo = list.Any(x => x == 2); //<-- throws exception.. (Enumeration changed) 
     if (foo == true) 
     { 
      //do something... 
     } 

     list.Add(i); 
    } 
} 

這個改變最終會破壞執行,告訴我枚舉已被修改。

我該如何讓這段代碼有效?

----併發名單----

public class ConcurrentList<T> : IList<T>, IList 
{ 
    private readonly List<T> underlyingList = new List<T>(); 
    private readonly object syncRoot = new object(); 
    private readonly ConcurrentQueue<T> underlyingQueue; 
    private bool requiresSync; 
    private bool isDirty; 

    public ConcurrentList() 
    { 
     underlyingQueue = new ConcurrentQueue<T>(); 
    } 

    public ConcurrentList(IEnumerable<T> items) 
    { 
     underlyingQueue = new ConcurrentQueue<T>(items); 
    } 

    private void UpdateLists() 
    { 
     if (!isDirty) 
      return; 
     lock (syncRoot) 
     { 
      requiresSync = true; 
      T temp; 
      while (underlyingQueue.TryDequeue(out temp)) 
       underlyingList.Add(temp); 
      requiresSync = false; 
     } 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
     lock (syncRoot) 
     { 
      UpdateLists(); 
      return underlyingList.GetEnumerator(); 
     } 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 

    public void Add(T item) 
    { 
     if (requiresSync) 
      lock (syncRoot) 
       underlyingQueue.Enqueue(item); 
     else 
      underlyingQueue.Enqueue(item); 
     isDirty = true; 
    } 

    public int Add(object value) 
    { 
     if (requiresSync) 
      lock (syncRoot) 
       underlyingQueue.Enqueue((T)value); 
     else 
      underlyingQueue.Enqueue((T)value); 
     isDirty = true; 
     lock (syncRoot) 
     { 
      UpdateLists(); 
      return underlyingList.IndexOf((T)value); 
     } 
    } 

    public bool Contains(object value) 
    { 
     lock (syncRoot) 
     { 
      UpdateLists(); 
      return underlyingList.Contains((T)value); 
     } 
    } 

    public int IndexOf(object value) 
    { 
     lock (syncRoot) 
     { 
      UpdateLists(); 
      return underlyingList.IndexOf((T)value); 
     } 
    } 

    public void Insert(int index, object value) 
    { 
     lock (syncRoot) 
     { 
      UpdateLists(); 
      underlyingList.Insert(index, (T)value); 
     } 
    } 

    public void Remove(object value) 
    { 
     lock (syncRoot) 
     { 
      UpdateLists(); 
      underlyingList.Remove((T)value); 
     } 
    } 

    public void RemoveAt(int index) 
    { 
     lock (syncRoot) 
     { 
      UpdateLists(); 
      underlyingList.RemoveAt(index); 
     } 
    } 

    T IList<T>.this[int index] 
    { 
     get 
     { 
      lock (syncRoot) 
      { 
       UpdateLists(); 
       return underlyingList[index]; 
      } 
     } 
     set 
     { 
      lock (syncRoot) 
      { 
       UpdateLists(); 
       underlyingList[index] = value; 
      } 
     } 
    } 

    object IList.this[int index] 
    { 
     get { return ((IList)this)[index]; } 
     set { ((IList)this)[index] = (T)value; } 
    } 

    public bool IsReadOnly 
    { 
     get { return false; } 
    } 

    public bool IsFixedSize 
    { 
     get { return false; } 
    } 

    public void Clear() 
    { 
     lock (syncRoot) 
     { 
      UpdateLists(); 
      underlyingList.Clear(); 
     } 
    } 

    public bool Contains(T item) 
    { 
     lock (syncRoot) 
     { 
      UpdateLists(); 
      return underlyingList.Contains(item); 
     } 
    } 

    public void CopyTo(T[] array, int arrayIndex) 
    { 
     lock (syncRoot) 
     { 
      UpdateLists(); 
      underlyingList.CopyTo(array, arrayIndex); 
     } 
    } 

    public bool Remove(T item) 
    { 
     lock (syncRoot) 
     { 
      UpdateLists(); 
      return underlyingList.Remove(item); 
     } 
    } 

    public void CopyTo(Array array, int index) 
    { 
     lock (syncRoot) 
     { 
      UpdateLists(); 
      underlyingList.CopyTo((T[])array, index); 
     } 
    } 

    public int Count 
    { 
     get 
     { 
      lock (syncRoot) 
      { 
       UpdateLists(); 
       return underlyingList.Count; 
      } 
     } 
    } 

    public object SyncRoot 
    { 
     get { return syncRoot; } 
    } 

    public bool IsSynchronized 
    { 
     get { return true; } 
    } 

    public int IndexOf(T item) 
    { 
     lock (syncRoot) 
     { 
      UpdateLists(); 
      return underlyingList.IndexOf(item); 
     } 
    } 

    public void Insert(int index, T item) 
    { 
     lock (syncRoot) 
     { 
      UpdateLists(); 
      underlyingList.Insert(index, item); 
     } 
    } 
} 

---- ---- MainWindow.xaml.cs

public MainWindow() 
{ 
    InitializeComponent(); 

    Console.WriteLine(@"standard List<T< - 10000 work items"); 
    var list1 = new ConcurrentList<int>(); 
    var start1 = DateTime.Now.Ticks; 
    DoWork(list1, 10000); 
    var end1 = DateTime.Now.Ticks; 
    var c1 = list1.Count; // accesses list 
    var cend1 = DateTime.Now.Ticks; 
    Console.WriteLine(); 
    Console.WriteLine(@"Work Time: {0} - milliseconds", (end1 - start1) 
     /TimeSpan.TicksPerMillisecond); 
    Console.WriteLine(@"Get Count Time: {0} - milliseconds", 
     (cend1 - end1) 
     /TimeSpan.TicksPerMillisecond); 
    Console.WriteLine(); 
    Console.WriteLine(); 

    Console.WriteLine(@"ConcurrentList<T> - 10000 work items on single thread"); 
    var list2 = new ConcurrentList<int>(); 
    var start2 = DateTime.Now.Ticks; 
    DoWork(list2, 10000); 
    var end2 = DateTime.Now.Ticks; 
    var c2 = list2.Count; // accesses list, update performed 
    var cend2 = DateTime.Now.Ticks; 
    Console.WriteLine(); 
    Console.WriteLine(@"Work Time: {0} - milliseconds", (end2 - start2) 
     /TimeSpan.TicksPerMillisecond); 
    Console.WriteLine(@"Get Count Time: {0} - milliseconds", 
     (cend2 - end2) 
     /TimeSpan.TicksPerMillisecond); 
    Console.WriteLine(); 
    Console.WriteLine(); 

    Console.WriteLine(@"ConcurrentList<T> - 10000 work items on 4 parallel tasks"); 
    var list3 = new ConcurrentList<int>(); 
    var start3 = DateTime.Now.Ticks; 
    var tasks3 = new[] 
     { 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
      Task.Factory.StartNew(() => DoWork(list3,10000)), 
     }; 
    Task.WaitAll(tasks3); 
    var end3 = DateTime.Now.Ticks; 
    var c3 = list3.Count; // accesses list, update performed 
    var cend3 = DateTime.Now.Ticks; 
    Console.WriteLine(); 
    Console.WriteLine(@"Work Time: {0} - milliseconds", (end3 - start3) 
     /TimeSpan.TicksPerMillisecond); 
    Console.WriteLine(@"Get Count Time: {0} - milliseconds", 
      (cend3 - end3) 
     /TimeSpan.TicksPerMillisecond); 
    Console.WriteLine(); 
} 

static void DoWork(ConcurrentList<int> list, int count) 
{ 
    for (var i = 0; i < count; i++) 
    { 
     var foo = list.Any(x => x == 2); //<--- throws Exception (Enumeration changed) 
     if (foo) 
     { 

     } 

     list.Add(Rnd.Next(int.MinValue, int.MaxValue)); 

     // use spinwait to emulate work but avoiding 
     // context switching 
     //Thread.SpinWait(100000); 
    } 
} 
+1

什麼是'list'? –

+3

你試過SynchronizedCollection http://msdn.microsoft.com/en-us/library/ms668265.aspx? – Artru

+0

@ DanielA.White你是否按照提供的鏈接? –

回答

3

這不是真正的多線程。

這裏的Any實現:

public static bool Any<TSource>(this IEnumerable<TSource> source) { 
     if (source == null) throw Error.ArgumentNull("source"); 
     using (IEnumerator<TSource> e = source.GetEnumerator()) { 
      // Suppose that another thread added an item here 
      // And obviously you get an InvalidOperationException 
      if (e.MoveNext()) return true; 
     } 
     return false; 
} 

該代碼會拋出同樣的異常:

var collection = new List<int>() { 0 }; 

foreach (var c in collection) { 
    collection.Add(c); 
} 

爲了使您的收藏線程安全的,你可以回到原來的集合的副本的枚舉,每GetEnumerator致電(這是從System.Collections.Concurrent行爲收集的方式):

public IEnumerator<T> GetEnumerator() 
{ 
    lock (syncRoot) 
    { 
     UpdateLists(); 
     return new List<T>(underlyingList).GetEnumerator(); 
    } 
} 
+0

那麼你是說沒有可能讓它線程安全嗎? –

+0

@RandRandom我已經更新了我的答案 – 2kay