J'utilise ConcurrentQueue
pour une structure de données partagée dont le but est de contenir les N derniers objets qui lui sont passés (genre d'histoire).
Supposons que nous ayons un navigateur et que nous souhaitons avoir les 100 dernières URL consultées. Je veux une file d'attente qui supprime automatiquement (dequeue) la plus ancienne (première) entrée lors de l'insertion d'une nouvelle entrée (mise en file d'attente) lorsque la capacité est pleine (100 adresses dans l'historique).
Comment puis-je accomplir cela en utilisant System.Collections
?
Réponses:
J'écrirais une classe wrapper qui, sur Enqueue, vérifierait le compte, puis Dequeue lorsque le nombre dépasse la limite.
la source
q
est privé pour l'objet, de sorte que lelock
empêchera les autres threads d'accéder simultanément.Count
et ceTryDequeue
sont deux opérations indépendantes qui ne sont pas synchronisées par BCL Concurrent.ConcurrentQueue<T>
objet contre unQueue<T>
objet plus léger.Enqueue
appelleront toujours la file d'attente d'origine. En d'autres termes, bien que cette réponse soit marquée comme acceptée, elle est complètement et complètement brisée.J'opterais pour une légère variante ... étendre ConcurrentQueue afin de pouvoir utiliser les extensions Linq sur FixedSizeQueue
la source
Pour tous ceux qui le trouvent utile, voici un code de travail basé sur la réponse de Richard Schneider ci-dessus:
la source
Pour ce que cela vaut, voici un tampon circulaire léger avec certaines méthodes marquées pour une utilisation sûre et non sécurisée.
J'aime utiliser la
Foo()/SafeFoo()/UnsafeFoo()
convention:Foo
appel de méthodesUnsafeFoo
par défaut.UnsafeFoo
Les méthodes modifient librement l'état sans verrou, elles ne devraient appeler que d'autres méthodes non sécurisées.SafeFoo
Les méthodes appellent desUnsafeFoo
méthodes à l'intérieur d'un verrou.C'est un peu verbeux, mais cela fait des erreurs évidentes, comme appeler des méthodes non sécurisées en dehors d'un verrou dans une méthode qui est censée être thread-safe, plus apparente.
la source
Voici mon point de vue sur la file d'attente de taille fixe
Il utilise une file d'attente régulière, pour éviter la surcharge de synchronisation lorsque la
Count
propriété est utiliséeConcurrentQueue
. Il implémente égalementIReadOnlyCollection
afin que les méthodes LINQ puissent être utilisées. Le reste est très similaire aux autres réponses ici.la source
Juste pour le plaisir, voici une autre implémentation qui, je crois, répond à la plupart des préoccupations des commentateurs. En particulier, la sécurité des threads est obtenue sans verrouillage et l'implémentation est masquée par la classe d'emballage.
la source
_queue.Enqueue(obj)
mais avantInterlocked.Increment(ref _count)
et que l'autre thread appelle.Count
? Ce serait un mauvais décompte. Je n'ai pas vérifié les autres problèmes.Ma version est juste une sous-classe de
Queue
celles normales ... rien de spécial mais voir tout le monde participer et cela va toujours avec le titre du sujet, je ferais aussi bien de le mettre ici. Il renvoie également les fichiers retirés de la file d'attente au cas où.la source
Ajoutons une autre réponse. Pourquoi cela par rapport aux autres?
1) Simplicité. Essayer de garantir la taille est bien, mais conduit à une complexité inutile qui peut présenter ses propres problèmes.
2) Implémente IReadOnlyCollection, ce qui signifie que vous pouvez utiliser Linq dessus et le transmettre à une variété de choses qui attendent IEnumerable.
3) Pas de verrouillage. La plupart des solutions ci-dessus utilisent des verrous, ce qui est incorrect sur une collection sans verrou.
4) Implémente le même ensemble de méthodes, propriétés et interfaces que ConcurrentQueue, y compris IProducerConsumerCollection, ce qui est important si vous souhaitez utiliser la collection avec BlockingCollection.
Cette implémentation pourrait potentiellement aboutir à plus d'entrées que prévu si TryDequeue échoue, mais la fréquence de ce qui se produit ne semble pas valoir un code spécialisé qui entravera inévitablement les performances et causera ses propres problèmes inattendus.
Si vous voulez absolument garantir une taille, implémenter une méthode Prune () ou similaire semble être la meilleure idée. Vous pouvez utiliser un verrou de lecture ReaderWriterLockSlim dans les autres méthodes (y compris TryDequeue) et prendre un verrou en écriture uniquement lors de l'élagage.
la source
Juste parce que personne ne l'a encore dit ... vous pouvez utiliser a
LinkedList<T>
et ajouter la sécurité des threads:Une chose à noter est que l'ordre d'énumération par défaut sera LIFO dans cet exemple. Mais cela peut être annulé si nécessaire.
la source
Pour votre plaisir de codage, je vous soumets le '
ConcurrentDeck
'Exemple d'utilisation:
la source
Eh bien, cela dépend de l'utilisation, j'ai remarqué que certaines des solutions ci-dessus peuvent dépasser la taille lorsqu'elles sont utilisées dans un environnement multithread. Quoi qu'il en soit, mon cas d'utilisation était d'afficher les 5 derniers événements et il y a plusieurs threads qui écrivent des événements dans la file d'attente et un autre thread qui la lit et l'affiche dans un contrôle Winform. C'était donc ma solution.
EDIT: Puisque nous utilisons déjà le verrouillage dans notre implémentation, nous n'avons pas vraiment besoin de ConcurrentQueue, cela peut améliorer les performances.
EDIT: Nous n'avons pas vraiment besoin
syncObject
dans l'exemple ci-dessus et nous pouvons plutôt utiliser unqueue
objet puisque nous ne réinitialisonsqueue
aucune fonction et qu'il est marqué comme dereadonly
toute façon.la source
La réponse acceptée va avoir des effets secondaires évitables.
Les liens ci-dessous sont des références que j'ai utilisées lorsque j'ai écrit mon exemple ci-dessous.
Bien que la documentation de Microsoft soit un peu trompeuse car ils utilisent un verrou, ils verrouillent cependant les classes de segment. Les classes de segment elles-mêmes utilisent Interlocked.
la source
Voici encore une autre implémentation qui utilise autant que possible le ConcurrentQueue sous-jacent tout en fournissant les mêmes interfaces disponibles via ConcurrentQueue.
la source
Voici ma version de la file d'attente:
Je trouve utile d'avoir un constructeur basé sur un IEnumerable et je trouve utile d'avoir un GetSnapshot pour avoir une liste sûre multithread (tableau dans ce cas) des éléments au moment de l'appel, qui ne monte pas erreurs si la collection sous-jacente change.
Le double contrôle de compte permet d'éviter le verrouillage dans certaines circonstances.
la source