web-dev-qa-db-fra.com

Parallel.ForEach avec ajout à la liste

J'essaie d'exécuter plusieurs fonctions qui se connectent à un site distant (par réseau) et renvoient une liste générique. Mais je veux les exécuter simultanément.

Par exemple:

public static List<SearchResult> Search(string title)
{
    //Initialize a new temp list to hold all search results
    List<SearchResult> results = new List<SearchResult>();

    //Loop all providers simultaneously
    Parallel.ForEach(Providers, currentProvider =>
    {
        List<SearchResult> tmpResults = currentProvider.SearchTitle((title));

        //Add results from current provider
        results.AddRange(tmpResults);
    });

    //Return all combined results
    return results;
}

Comme je le vois, plusieurs insertions dans les "résultats" peuvent se produire en même temps ... Ce qui peut planter mon application.

Comment puis-je éviter ça?

67
shaharmor
//In the class scope:
Object lockMe = new Object();    

//In the function
lock (lockMe)
{    
     results.AddRange(tmpResults);
}

Fondamentalement, un verrou signifie qu'un seul thread peut avoir accès à cette section critique en même temps.

49
Haedrian

Vous pouvez utiliser un collecte simultanée .

Le System.Collections.Concurrent namespace fournit plusieurs classes de collection thread-safe qui doivent être utilisées à la place des types correspondants dans le System.Collections et System.Collections.Generic _ espaces de noms chaque fois que plusieurs threads accèdent simultanément à la collection.

Vous pouvez par exemple utiliser ConcurrentBag puisque vous n'avez aucune garantie quant à l'ordre dans lequel les éléments seront ajoutés.

Représente une collection d'objets sécurisée par les threads et non ordonnée.

134
Mark Byers

Les collections simultanées sont nouvelles pour .Net 4; ils sont conçus pour fonctionner avec la nouvelle fonctionnalité parallèle.

Voir Collections simultanées dans le .NET Framework 4 :

Avant .NET 4, vous deviez fournir vos propres mécanismes de synchronisation si plusieurs threads pouvaient accéder à une seule collection partagée. Vous deviez verrouiller la collection ...

... les [nouvelles] classes et interfaces de System.Collections.Concurrent [ajouté dans .NET 4] fournissent une implémentation cohérente pour les problèmes de [...] programmation multithread impliquant des données partagées entre threads.

22
arootbeer

Pour ceux qui préfèrent le code:

public static ConcurrentBag<SearchResult> Search(string title)
{
    var results = new ConcurrentBag<SearchResult>();
    Parallel.ForEach(Providers, currentProvider =>
    {
        results.Add(currentProvider.SearchTitle((title)));
    });

    return results;
}
21
Matas Vaitkevicius

Ceci pourrait être exprimé de manière concise en utilisant AsParallel et SelectMany:

public static List<SearchResult> Search(string title)
{
    return Providers.AsParallel()
                    .SelectMany(p => p.SearchTitle(title))
                    .ToList();
}
12
Douglas