33 votes

Parallèle.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 faire planter mon application.

Comment puis-je éviter cela ?

54voto

Mark Byers Points 318575

Vous pouvez utiliser un collection simultanée .

Le site System.Collections.Concurrent fournit plusieurs classes de collection à sécurité thread qui doivent être utilisées à la place des types correspondants dans l'espace de nommage de l'utilisateur. System.Collections et System.Collections.Generic lorsque plusieurs threads accèdent à la collection simultanément.

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 non ordonnée, à l'abri des fils.

35voto

LIUFA Points 3642

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;
}

26voto

Haedrian Points 2933
//In the class scope:
Object lockMe = new Object();    

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

En fait, un verrou signifie qu'un seul thread peut avoir accès à cette section critique au même moment.

15voto

arootbeer Points 2973

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

Ver Collections concurrentes dans le .NET Framework 4 :

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

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

15voto

Douglas Points 25145

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

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X