Avec le nouveau ConcurrentBag<T>
dans .NET 4, comment enlevez-vous un certain objet spécifique lorsque seuls TryTake()
et TryPeek()
sont disponibles?
Je songe à utiliser TryTake()
, puis à rajouter simplement l'objet résultant dans la liste si je ne pas veux le supprimer, mais j'ai le sentiment que quelque chose me manque. Est-ce la bonne façon?
La réponse courte: vous ne pouvez pas le faire facilement.
Le ConcurrentBag conserve une file d'attente locale de threads pour chaque thread et il ne regarde que les files d'attente des autres threads une fois que sa propre file d'attente est vide. Si vous supprimez un élément et le remettez en place, l'élément suivant que vous supprimez peut être à nouveau le même. Rien ne garantit que le fait de supprimer et de remettre à plusieurs reprises des éléments vous permettra de parcourir tous les éléments.
Deux alternatives pour vous:
Tu ne peux pas. C'est un sac, il n'est pas commandé. Lorsque vous le remettez, vous restez coincé dans une boucle sans fin.
Vous voulez un ensemble. Vous pouvez en émuler un avec ConcurrentDictionary. Ou un HashSet que vous vous protégez avec un verrou.
Le ConcurrentBag est idéal pour gérer une liste dans laquelle vous pouvez ajouter des éléments et énumérer de nombreux threads, puis éventuellement le jeter comme son nom l'indique :)
Comme Mark Byers l'a dit , 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 hits de threads à l'aide d'un verrou. Ceci est un one-liner:
myBag = new ConcurrentBag<Entry>(myBag.Except(new[] { removedEntry }));
Cela fonctionne et correspond à l'esprit dans lequel le ConcurrentBag a été conçu.
Comme vous le mentionnez, TryTake()
est la seule option. C'est également l'exemple sur MSDN . Le réflecteur ne montre aucune autre méthode d’intérêt interne cachée.
Mark est correct car le ConcurrentDictionary is fonctionnera comme vous le souhaitez. Si vous souhaitez toujours utiliser un ConcurrentBag ce qui suit, pas très efficace, vous y arrivera.
var stringToMatch = "test";
var temp = new List<string>();
var x = new ConcurrentBag<string>();
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<T>(this ConcurrentBag<T> bag, T item)
{
while (bag.Count > 0)
{
T result;
bag.TryTake(out result);
if (result.Equals(item))
{
break;
}
bag.Add(result);
}
}
Essayez BlockingCollection<T>
à la place. Disponible en .Net Core.
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 une liste d’articles du sac
public static class ConcurrentBag
{
static Object locker = new object();
public static void Clear<T>(this ConcurrentBag<T> bag)
{
bag = new ConcurrentBag<T>();
}
public static void Remove<T>(this ConcurrentBag<T> bag, List<T> itemlist)
{
try
{
lock (locker)
{
List<T> removelist = bag.ToList();
Parallel.ForEach(itemlist, currentitem => {
removelist.Remove(currentitem);
});
bag = new ConcurrentBag<T>();
Parallel.ForEach(removelist, currentitem =>
{
bag.Add(currentitem);
});
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
public static void Remove<T>(this ConcurrentBag<T> bag, T removeitem)
{
try
{
lock (locker)
{
List<T> removelist = bag.ToList();
removelist.Remove(removeitem);
bag = new ConcurrentBag<T>();
Parallel.ForEach(removelist, currentitem =>
{
bag.Add(currentitem);
});
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
}