2010-12-01 1 views
1

TPLで作成されたスレッドがいくつかあります。いくつかのスレッドは、何も変更することなく、含まれているListを単純に列挙することによって、特定のオブジェクトとやりとりします。他のスレッドはリストに項目を追加または削除します。現在、Listを変更するすべてのコードセグメントのListおよびLockステートメントを列挙するすべてのコードセグメントについて、lockステートメントがあります。C#TPL複数のリストの列挙子飢餓のない1つの修飾子

パフォーマンス上の問題は発生していません。そうです。しかし、私は効率的な解決策は多くの並行列挙子を許可し、リストを変更するときは他のすべてをロックすることであることを認識しています。現在、常に1つのスレッドのみがリストを列挙している可能性があります。将来の参照のために、これを可能にするパターンは何ですか?

重要。多くのケースでうまくいく多くのソリューションがありますが、私の場合はほとんど機能しません。私のアプリケーションでは、読者の弾圧が決して止まらない可能性が高いので、すべての修飾語が飢えています。私は最近、同時リストを実装しなければならなかった、とあなたはそれが助け場合はそれを試してみることが歓迎されている

Enumerate 1 
Enumerate 2 Concurrent with 1 
Modify 1 Request is Queued 
Enumerate 3 Request is Queued because of Modify Request 
Enumerate 4 Request is Queued 
Modify 2 Request is Queued 
Enumerate 2 Finishes 
Enumerate 1 Finishes 
Modify 1 Starts because all in-progress at time of request Enumerators Finished 
Modify 1 Finishes 
Enumerate 3 Starts because Queued Modify 1 Finished 
Enumerate 4 Starts 
Enumerate 3 Finishes 
Enumerate 4 Finishes 
Modify 2 Starts 
... 
+0

コードの変更が必要なので、ConcurrentDictionaryに切り替えるのではないでしょうか? – weismat

答えて

2

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.ToList().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<T>)this)[index]; } 
     set { ((IList<T>)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); 
     } 
    } 
} 
+0

これを使用するときは非常に注意してください!このリストを安全に列挙することは不可能です。カウントは読者の下から変更することができます。 (また、ConcurrentQueueを使って何をしているのかわからない、なぜrequiresSyncフラグが必要なのでしょうか?ConcurrentQueueは常にスレッドセーフです。) –

2

問題への古典的な解決策がreader-である私が探していますライターロック。リーダライタのロックでは、他のリーダがロックを取らないようにしますが、ライタは読者や他のライタをブロックするので、安全に変更できます。

TPLを使用している場合は、確かにSystem.Threading.ReaderWriterLockSlimにアクセスできます。リストを列挙するときはリーダーロックを取り、リストに項目を追加または削除するときはライターロックを取る。これにより、リストのスケーラビリティの「次のレベル」が得られます。

+0

このタイプのロックは、 。書き込み要求はキューに入れられ、読み取り要求の前に来ますか?あるいは、彼らは飢えてしまいますか? – colithium

+0

ReaderWriterLockSlimクラスは "Fair"です。つまり、ライターがロックを待機すると、ライターの要求が満たされるまで、さらに読み取りがブロックされます。だから、飢えを心配する必要はありません。 –