Comment supprimer un seul object spécifique d’un ConcurrentBag ?

Avec le nouveau ConcurrentBag dans .NET 4, comment enlevez-vous un certain object spécifique lorsque seulement TryTake() et TryPeek() sont disponibles?

Je pense à utiliser TryTake() puis à rappend simplement l’object résultant dans la liste si je ne veux pas le supprimer, mais j’ai l’impression que quelque chose me manque. C’est la bonne route?

La réponse courte: vous ne pouvez pas le faire facilement.

Le ConcurrentBag conserve une queue locale de thread pour chaque thread et ne regarde que les files d’attente des autres threads une fois que sa propre queue est vide. Si vous supprimez un article et le replacez, l’élément suivant que vous supprimez peut être le même. Il n’y a aucune garantie que supprimer des éléments à plusieurs resockets et les remettre vous permettra de parcourir tous les éléments.

Deux alternatives pour vous:

  • Supprimez tous les éléments et rappelez-les, jusqu’à ce que vous trouviez celui que vous souhaitez supprimer, puis remettez les autres après. Notez que si deux threads essaient de faire cela simultanément, vous aurez des problèmes.
  • Utilisez une structure de données plus appropriée telle que ConcurrentDictionary .

Vous ne pouvez pas C’est un sac, il n’est pas commandé. Lorsque vous le remettez, vous vous retrouvez coincé dans une boucle sans fin.

Vous voulez un ensemble. Vous pouvez en émuler un avec ConcurrentDictionary. Ou un HashSet que vous protégez vous-même avec un verrou.

Comme vous le mentionnez, TryTake() est la seule option. C’est aussi l’exemple sur MSDN . Reflector ne montre pas non plus d’autres méthodes internes cachées.

Le ConcurrentBag est génial pour gérer une liste où vous pouvez append des éléments et énumérer à partir de nombreux threads, puis éventuellement les jeter comme son nom l’indique 🙂

Comme l’indique Mark Byers , vous pouvez reconstruire un nouveau ConcurrentBag qui ne contient pas l’élément que vous souhaitez supprimer, mais vous devez le protéger contre plusieurs access de threads à l’aide d’un verrou. Ceci est un one-liner:

 myBag = new ConcurrentBag(myBag.Except(new[] { removedEntry })); 

Cela fonctionne et correspond à l’esprit dans lequel le ConcurrentBag a été conçu.

Mark est correct en ce sens que le ConcurrentDictionary fonctionne comme vous le souhaitez. Si vous souhaitez toujours utiliser un ConcurrentBag, les éléments suivants, non efficaces, vous y conduiront.

 var ssortingngToMatch = "test"; var temp = new List(); var x = new ConcurrentBag(); for (int i = 0; i < 10; i++) { x.Add(string.Format("adding{0}", i)); } string y; while (!x.IsEmpty) { x.TryTake(out y); if(string.Equals(y, stringToMatch, StringComparison.CurrentCultureIgnoreCase)) { break; } temp.Add(y); } foreach (var item in temp) { x.Add(item); } 
 public static void Remove(this ConcurrentBag bag, T item) { while (bag.Count > 0) { T result; bag.TryTake(out result); if (result.Equals(item)) { break; } bag.Add(result); } } 

Ceci est ma classe d’extension que j’utilise dans mes projets. Il peut supprimer un seul élément de ConcurrentBag et peut également supprimer la liste des articles du sac

 public static class ConcurrentBag { static Object locker = new object(); public static void Clear(this ConcurrentBag bag) { bag = new ConcurrentBag(); } public static void Remove(this ConcurrentBag bag, List itemlist) { try { lock (locker) { List removelist = bag.ToList(); Parallel.ForEach(itemlist, currentitem => { removelist.Remove(currentitem); }); bag = new ConcurrentBag(); Parallel.ForEach(removelist, currentitem => { bag.Add(currentitem); }); } } catch (Exception ex) { Debug.WriteLine(ex.Message); } } public static void Remove(this ConcurrentBag bag, T removeitem) { try { lock (locker) { List removelist = bag.ToList(); removelist.Remove(removeitem); bag = new ConcurrentBag(); Parallel.ForEach(removelist, currentitem => { bag.Add(currentitem); }); } } catch (Exception ex) { Debug.WriteLine(ex.Message); } } } 
 public static ConcurrentBag RemoveItemFromConcurrentBag(ConcurrentBag Array, Ssortingng Item) { var Temp=new ConcurrentBag(); Parallel.ForEach(Array, Line => { if (Line != Item) Temp.Add(Line); }); return Temp; } 

que diriez-vous:

 bag.Where(x => x == item).Take(1); 

Ca marche, je ne sais pas avec quelle efficacité …