Параллельный.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;
}

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

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

5 53

5 ответов:

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

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

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

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

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

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

представляет потокобезопасную, неупорядоченную коллекцию объектов.

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

посмотреть параллельные коллекции в .NET Framework 4:

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

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

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

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

    return results;
}

Это может быть выражено лаконично, используя и SelectMany:

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