Création d'une File d'attente de blocage<T> dans .NET?
J'ai un scénario où j'ai plusieurs threads ajout d'une file d'attente et plusieurs threads de lecture à partir de la même file d'attente. Si la file d'attente atteint une taille spécifique tous les threads qui sont le remplissage de la file d'attente sera bloqué sur ajouter jusqu'à un point est retiré de la file d'attente.
La solution ci-dessous est ce que je suis en ce moment et ma question est: Comment cela peut-il être amélioré? Est-ce un objet qui permet déjà ce comportement dans la BCL que je devrais utiliser?
internal class BlockingCollection<T> : CollectionBase, IEnumerable
{
//todo: might be worth changing this into a proper QUEUE
private AutoResetEvent _FullEvent = new AutoResetEvent(false);
internal T this[int i]
{
get { return (T) List[i]; }
}
private int _MaxSize;
internal int MaxSize
{
get { return _MaxSize; }
set
{
_MaxSize = value;
checkSize();
}
}
internal BlockingCollection(int maxSize)
{
MaxSize = maxSize;
}
internal void Add(T item)
{
Trace.WriteLine(string.Format("BlockingCollection add waiting: {0}", Thread.CurrentThread.ManagedThreadId));
_FullEvent.WaitOne();
List.Add(item);
Trace.WriteLine(string.Format("BlockingCollection item added: {0}", Thread.CurrentThread.ManagedThreadId));
checkSize();
}
internal void Remove(T item)
{
lock (List)
{
List.Remove(item);
}
Trace.WriteLine(string.Format("BlockingCollection item removed: {0}", Thread.CurrentThread.ManagedThreadId));
}
protected override void OnRemoveComplete(int index, object value)
{
checkSize();
base.OnRemoveComplete(index, value);
}
internal new IEnumerator GetEnumerator()
{
return List.GetEnumerator();
}
private void checkSize()
{
if (Count < MaxSize)
{
Trace.WriteLine(string.Format("BlockingCollection FullEvent set: {0}", Thread.CurrentThread.ManagedThreadId));
_FullEvent.Set();
}
else
{
Trace.WriteLine(string.Format("BlockingCollection FullEvent reset: {0}", Thread.CurrentThread.ManagedThreadId));
_FullEvent.Reset();
}
}
}
- .Net comment construit-dans les classes pour aider à ce scénario. La plupart des réponses énumérées ici sont obsolètes. Voir les plus récentes réponses au bas. Regardez dans les thread-safe blocage des collections. Les réponses peuvent être obsolètes, mais c'est toujours une bonne question!
- Je pense que c'est toujours une bonne idée d'apprendre sur le Moniteur.Wait/Impulsion/PulseAll même si nous avons des nouvelles concurrentes des classes dans .NET.
- D'accord avec @thewpfguy. Vous aurez envie de comprendre la base des mécanismes de verrouillage de derrière les coulisses. Également intéressant de noter que les Systèmes.Les Collections.Simultanées n'existait pas jusqu'en avril 2010, et alors seulement dans Visual Studio 2010 et au-dessus. Définitivement pas une option pour le VS2008 tenir les aboutissants...
- Si vous lisez ceci maintenant, jetez un oeil au Système.Le filetage.Les canaux multi-écrivain/multi-lecteur, borné, éventuellement le blocage de la mise en œuvre de ce pour .NET de Base et .NET Standard.
Vous devez vous connecter pour publier un commentaire.
Qui a l'air très dangereux (très peu de synchronisation); comment quelque chose comme:
(edit)
En réalité, vous voulez un moyen de fermer la file d'attente afin que les lecteurs commencer à sortir proprement - peut-être quelque chose comme un bool drapeau - le cas échéant, une vide la file d'attente retourne juste (plutôt que le blocage):
Wait
, de sorte que les autres threads peuvent l'acquérir. Il libère le verrou(s) quand il se réveille.Enqueue
appelsPulseAll
il libère le moniteur à chaque thread en attente et la course pour verrouiller le moniteur de l'objet de nouveau, est-il possible que si un autre écrivain va gagner et de dire que la file d'attente est pleine à ce moment, il va entrerMonitor.Wait
de nouveau et puis nous nous retrouvons avec un blocage?.Wait
libère le verrou!! Le sens parfait maintenant. Thx.bool TryEnqueue(T item)
est aussi nécessaire à la sortie correctement les threads bloqués une foisqueue.Count
atteintmaxSize
. Les éléments dequeue
sera pas être consommés, mais ne peut rien faire beaucoup parler.Utilisation .net 4 BlockingCollection, de mise en file d'utiliser la fonction Ajouter(), à la résorption de l'utilisation de(). Il utilise en interne non bloquant ConcurrentQueue. Plus d'info ici Rapide et Meilleur Producteur/consommateur file d'attente de la technique BlockingCollection vs simultanées de la File d'attente
"Comment cela peut-il être amélioré?"
Bien, vous avez besoin de regarder toutes les méthodes de votre classe et de demander ce qui se passerait si un autre thread a été simultanément l'appel de cette méthode ou toute autre méthode. Par exemple, vous avez mis un verrou dans la méthode Remove, mais pas dans la méthode Add. Qu'advient-il si un thread Ajoute en même temps qu'un autre thread Supprime? De mauvaises choses.
Considèrent également qu'une méthode peut retourner une deuxième objet qui permet d'accéder à la première de l'objet de données internes - par exemple, GetEnumerator. Imaginez un fil traverse que les agents recenseurs, un autre thread, c'est de modifier la liste en même temps. Pas bon.
Une bonne règle de base est de faire le plus simple pour obtenir le droit de couper le nombre de méthodes dans la classe au minimum absolu.
En particulier, ne pas hériter d'une autre classe de conteneur, parce que vous allez vous exposer l'ensemble de cette classe de méthodes, fournissant un moyen pour l'appelant de corrompre la base de données interne, ou de voir partiellement effectuer les modifications apportées aux données (tout aussi mauvais, parce que les données s'affiche corrompu à ce moment). Masquer tous les détails et de se montrer impitoyable à propos de la façon dont vous autorisez l'accès à eux.
J'aimerais vous conseillons fortement d'utiliser de solutions prêtes à l'emploi - un livre sur le filetage ou utiliser la 3ème partie de la bibliothèque. Autrement, compte tenu de ce que vous tentez, vous allez être le débogage du code pour une longue période de temps.
Aussi, ne serait-il pas plus logique de Supprimer le retour d'un élément (par exemple, celui qui a été ajouté en premier, car il est une file d'attente), plutôt que de l'appelant de choisir un élément spécifique? Et lorsque la file d'attente est vide, peut-être Supprimer doit également bloquer.
Mise à jour: la réponse de Marc en fait met en œuvre toutes ces suggestions! 🙂 Mais je vais laisser cela ici comme il peut être utile de comprendre pourquoi sa version est une amélioration.
Vous pouvez utiliser le BlockingCollection et ConcurrentQueue dans le Système.Les Collections.Simultanée De L'Espace De Noms
Je viens juste d'en cela de l'aide du Réactif Extensions et de rappeler à cette question:
Pas nécessairement entièrement sûr, mais très simple.
C'est ce que je suis venu op pour une thread-safe délimitée bloque la file d'attente.
Je n'ai pas encore totalement exploré la TPL mais ils pourraient avoir quelque chose qui correspond à vos besoins, ou à tout le moins, certains Réflecteur de fourrage pour accrocher l'inspiration.
Espère que ça aide.
Bien, vous pourriez regarder
System.Threading.Semaphore
classe. Autre que cela - pas, vous avez à faire vous-même. Autant que je sache, il n'y a pas une telle collection intégrée.Si vous souhaitez le maximum de débit, permettant à de multiples lecteurs à lire et à seulement un écrivain à écrire, la BCL a quelque chose qui s'appelle ReaderWriterLockSlim qui devrait l'aider à mincir votre code...