Подтвердить что ты не робот

Parallel.ForEach с добавлением в список

Я пытаюсь запустить несколько функций, которые подключаются к удаленному сайту (по сети) и возвращать общий список. Но я хочу запускать их одновременно.

Например:

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

Как я вижу, несколько одновременных входов в "результаты" могут произойти... Что может привести к сбою моего приложения.

Как я могу избежать этого?

4b9b3361

Ответ 1

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

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

В основном блокировка означает, что только один поток может иметь доступ к этому критическому разделу в одно и то же время.

Ответ 2

Вы можете использовать параллельную коллекцию .

Пространство имен System.Collections.Concurrent предоставляет несколько потокобезопасных классов коллекций, которые должны использоваться вместо соответствующих типов в пространствах имен System.Collections и System.Collections.Generic, когда несколько потоков обращаются к коллекции одновременно.

Вы можете, например, использовать ConcurrentBag, так как у вас нет гарантии, какой заказ будут добавлены.

Представляет собой потокобезопасный, неупорядоченный набор объектов.

Ответ 3

Параллельные коллекции новы для .Net 4; они предназначены для работы с новой параллельной функциональностью.

См. Параллельные коллекции в .NET Framework 4:

До .NET 4 вам пришлось предоставить свои собственные механизмы синхронизации, если несколько потоков могут иметь доступ к одной общей коллекции. Вам нужно было заблокировать коллекцию...

... [новые] классы и интерфейсы в System.Collections.Concurrent [добавлены в .NET 4] обеспечивают согласованную реализацию для [...] задач многопоточного программирования, связанных с общими данными по потокам.

Ответ 4

Для тех, кто предпочитает код:

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

    return results;
}

Ответ 5

Это можно было бы кратко выразить с помощью PLINQ AsParallel и SelectMany:

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