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;
}
Как я вижу, несколько одновременных входов в "результаты" могут произойти... Что может привести к сбою моего приложения.
Как я могу избежать этого?
Ответы
Ответ 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();
}