2010-12-01 47 views
1

J'ai plusieurs threads créés par le TPL (probablement pas pertinent). Certains threads interagissent avec un objet particulier simplement en énumérant une liste contenue sans jamais modifier quoi que ce soit. D'autres threads ajoutent ou suppriment des éléments de la liste. Actuellement, j'ai une instruction de verrouillage autour de tous les segments de code qui énumèrent les instructions List et lock autour de tous les segments de code qui modifient la liste. Je n'ai pas de problèmes de performance, c'est tout. Cependant, je me rends compte que la solution la plus efficace consisterait à autoriser plusieurs énumérateurs simultanés et à ne verrouiller que tout le reste lors de la modification de la liste. Actuellement, un seul thread peut être en train d'énumérer la liste à un moment donné. Pour référence future, quel est le modèle qui permet cela?C# Liste des énumérateurs multiples de liste TPL Un modificateur sans mésaventure

Important. Il y a beaucoup de solutions qui fonctionnent bien dans beaucoup de cas mais très probablement ne fonctionneront pas pour le mien. Dans mon application, il y a de fortes chances que le barrage des lecteurs ne s'arrête jamais - affamant ainsi tous les modificateurs. Je cherche:

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

Je suppose que vous n'êtes pas passer à ConcurrentDictionary, car cela nécessiterait des modifications de code - non? – weismat

Répondre

2

je devais mettre en œuvre une liste concurrente récemment, et nous vous invitons à l'essayer si elle aide:

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

Soyez très prudent lors de l'utilisation! Il est impossible d'énumérer cette liste en toute sécurité, car le nombre peut changer sous un lecteur. (De plus, je ne suis pas sûr de ce que vous faites avec la ConcurrentQueue ici, pourquoi avez-vous besoin du drapeau requiresSync? ConcurrentQueue est toujours thread-safe.) –

2

La solution classique au problème est Reader- verrous d'écrivain. Avec les verrous de lecteur-rédacteur, les lecteurs n'empêchent pas les autres lecteurs de prendre le verrou, mais les rédacteurs bloquent les lecteurs et autres rédacteurs, afin qu'ils puissent effectuer des changements en toute sécurité.

Si vous utilisez le TPL, vous avez certainement accès à System.Threading.ReaderWriterLockSlim. Prenez un verrou de lecteur lors de l'énumération de la liste, et prenez un verrou d'écriture lorsque vous ajoutez ou supprimez des éléments de la liste. Cela vous donnera le "niveau suivant" d'évolutivité sur la liste.

+0

Je croyais que ce type de verrou souffrait du problème que j'ai mentionné . Est-ce que les demandes d'écriture sont mises en file d'attente et viennent avant les demandes de lecture? Ou sont-ils juste affamés? – colithium

+0

La classe ReaderWriterLockSlim est "Fair", c'est-à-dire qu'une fois qu'un writer commence à attendre le verrou, d'autres lecteurs sont bloqués jusqu'à ce que la demande de l'auteur soit satisfaite. Vous n'avez donc pas à vous soucier de la famine. –