Existe-t-il un moyen d'ajouter plusieurs éléments à ConcurrentBag à la fois, au lieu d'un à la fois? Je ne vois pas de méthode AddRange () sur ConcurrentBag, mais il y a un Concat (). Cependant, cela ne fonctionne pas pour moi:
ConcurrentBag<T> objectList = new ConcurrentBag<T>();
timeChunks.ForEach(timeChunk =>
{
List<T> newList = Foo.SomeMethod<T>(x => x.SomeReadTime > timeChunk.StartTime);
objectList.Concat<T>(newList);
});
Ce code était dans un Parallel.ForEach (), mais je l'ai changé pour ce qui précède afin que je puisse le dépanner. La variable newList contient en effet des objets, mais après la ligne objectList.Concat <>, objectList contient toujours 0 objet. Concat <> ne fonctionne-t-il pas ainsi? Dois-je ajouter des éléments à ConcurrentBag un à la fois, avec la méthode Add ()?
Oui :)
Concat
est peut-être l'une des extensions Enumerable
. Il n'ajoute rien au ConcurrentBag
, il retourne juste un objet funky contenant le sac d'origine et tout ce que vous avez essayé d'y ajouter.
Attention, le résultat de Concat
n'est plus un ConcurrentBag
, donc vous ne voudriez pas l'utiliser. Cela fait partie du cadre général de LINQ, permettant de combiner des séquences immuables. Ce cadre, bien sûr, n'essaye pas d'étendre les propriétés simultanées des opérandes au résultat, donc l'objet résultant ne sera pas aussi bien adapté à un accès multithread.
(Fondamentalement, Concat
s'applique à ConcurrentBag
car il expose IEnumerable<T>
interface.)
(Je sais que c'est un vieux post, j'ai pensé ajouter un petit quelque chose).
Comme d'autres l'ont dit: oui, vous devez les ajouter un par un. Dans mon cas, j'ai ajouté une petite méthode d'extension pour rendre les choses un peu plus propres, mais sous le capot, cela fait la même chose:
public static void AddRange<T>(this ConcurrentBag<T> @this, IEnumerable<T> toAdd)
{
foreach (var element in toAdd)
{
@this.Add(element);
}
}
Puis:
ConcurrentBag<int> ccBag = new ConcurrentBag<int>();
var listOfThings = new List<int>() { 1, 2, 4, 5, 6, 7, 8, 9 };
ccBag.AddRange(listOfThings);
J'ai également envisagé d'utiliser AsParallel pour ajouter dans la méthode d'extension, mais après avoir exécuté des tests sur l'ajout d'une liste de chaînes de différentes tailles, il était toujours plus lent d'utiliser AsParallel (comme indiqué ici) par opposition à la boucle for traditionnelle.
public static void AddRange<T>(this ConcurrentBag<T> @this, IEnumerable<T> toAdd)
{
toAdd.AsParallel().ForAll(t => @this.Add(t));
}
Concat
est une méthode d'extension fournie par LINQ. Il s'agit d'une opération immuable qui renvoie un autre IEnumerable
qui peut énumérer la collection source suivie immédiatement par la collection spécifiée. Cela ne change en rien la collection source.
Vous devrez ajouter vos articles à la ConcurrentBag
une à la fois.
J'ai rencontré un problème similaire, en essayant de traiter de plus petits morceaux de données en parallèle, car un gros morceau expirait le service Web que j'utilisais pour accéder à mes données du côté de l'envoi, mais je ne voulais pas que les choses se déroulent plus lentement en traitant chaque morceau en série. Le traitement des données enregistrement par enregistrement a été encore plus lent - étant donné que le service que j'appelais pouvait traiter les demandes en masse, il serait préférable de soumettre autant de données que possible sans expirer.
Comme Vlad l'a dit, concaténer un sac simultané dans une liste d'un type d'objet ne renvoie pas un sac simultané, donc la concaténation ne fonctionnera pas! (Il m'a fallu un certain temps pour réaliser que je ne pouvais pas faire ça.)
Essayez ceci à la place - créez un List<T>
, puis créez un ConcurrentBag<List<T>>
. À chaque itération parallèle, il ajoutera une nouvelle liste au sac simultané. Lorsque la boucle parallèle est terminée, parcourez ConcurrentBag
et concat (ou l'union si vous voulez éliminer les doublons possibles) vers le premier List<T>
que vous avez créé pour "aplatir" tout dans une seule liste.
La méthode Concat
est une approche contenue dans public Enumerable
classe statique qui supporte = System.Linq
bibliothèque (interne .NET System.Core
Assemblage).
MAIS, le Concat
contient des limites pour fournir l'exigence "ajouter une plage" à ConcurrentBag<T>
objet, avec un comportement comme image ci-dessous (à la ligne 47 dans Visual Studio):
Pour que la méthode Concat
corresponde à l'exigence "ajouter une plage", il est nécessaire de renouveler l'actuel ConcurrentBag<T>
instance d'objet; si le programme a besoin d'ajouter plusieurs plages, il est nécessaire de créer des instances de référence automatique à partir du courant ConcurrentBag<T>
(récursivement) pour chaque plage.
Ensuite, je n'utilise pas l'approche Concat
et si je peux faire une recommandation, JE NE RECOMMANDE PAS. Je suis un exemple similaire de Eric réponse, où j'ai développé une classe dérivée de ConcurrentBag<T>
qui me fournit la méthode AddRange en utilisant ConcurrentBag<T>
méthode de base pour ajouter IEnumerable<T>
éléments de l'instance dérivée, comme ci-dessous:
public class ConcurrentBagCompleted<T> : ConcurrentBag<T> { public ConcurrentBagCompleted() : base() { } public ConcurrentBagCompleted(IEnumerable<T> collection):base(collection) { } public void AddRange(IEnumerable<T> collection) { Parallel.ForEach(collection, item => { base.Add(item); }); } }