Параллелизм не параллелизм.Параллельность не асинхронность.Параллельный запуск нескольких медленных запросов не заставит их работать быстрее, скорее наоборот.Это разные проблемы и требуют совершенно разных решений.Без конкретной проблемы можно дать только общий совет.
Параллелизм - обработка массива элементов размером 800К
Параллелизм означает параллельную обработку тонны данных с использованием нескольких ядер.Для этого вам нужно разделить ваши данные и передать каждый раздел «работнику» для обработки.Чтобы добиться максимальной производительности, вам нужно свести к минимуму общение между работниками и синхронизацию, иначе ваши работники будут тратить процессорное время, ничего не делая.Это значит, что глобальное обновление очереди не требуется.
Если у вас много строк или если обработка строк связана с ЦП, вы можете использовать PLINQ для ее обработки:
var query = from y in lines.AsParallel()
from t in GetTasks(y.LineCode)
select t;
var theResults=query.ToList();
Вот и все,Нет необходимости синхронизировать доступ к очереди, либо с помощью блокировки, либо с помощью одновременной коллекции.Это будет использовать все доступные ядра, хотя.Вы можете добавить WithDegreeOfParallelism()
, чтобы уменьшить количество ядер, используемых во избежание зависания
Параллелизм - вызов 2000 серверов
Параллельность с другой сторонызначит делать несколько разных вещей одновременно.Никакого разделения не происходит.
Например, если бы мне пришлось запрашивать данные мониторинга на 8 или 2000 серверах (правдивая история), я бы не использовал Parallel
или PLINQ.С одной стороны, Parallel
и PLINQ используют все доступные ядра.В этом случае, хотя они ничего не будут делать, они просто будут ждать ответов.Классы параллелизма также не могут обрабатывать асинхронные методы, потому что в этом нет никакого смысла - они не предназначены для ожидания ответов.
Очень быстрым и грязным решением было бы запустить несколько задач и ждатьдля их возврата, например:
var tasks=lines.Select(y=>Task.Run(()=>GetTasks(y.LineCode));
//Array of individual results
var resultsArray=await Task.WhenAll(tasks);
//flatten the results
var resultList=resultsArray.SelectMany(r=>r).ToList();
Это запустит все запросы сразу.Network Security не нравились одновременные запросы 2000 года, поскольку это выглядело как хакерская атака и вызвало небольшое наводнение сети.
Параллелизм с потоком данных
Мы можем использовать библиотеку потоков данных TPL и, например, ActionBlock или TransformBlock для выполнения запросов с контролируемой степенью параллелизма:
var options=new ExecutionDataflowBlockOptions {
MaxDegreeOfParallelism = 4 ,
BoundedCapacity=10,
};
var spamBlock=new TransformManyBlock<Line,Result>(
y=>GetTasks(y.LineCode),
options);
var outputBlock=new BufferBlock<Result>();
spamBlock.LinkTo(outputBlock);
foreach(var line in lines)
{
await spamBlock.SendAsync(line);
}
spamBlock.Complete();
//Wait for all 4 workers to finish
await spamBlock.Completion;
По завершении spamBlock
результаты можно найти в outputBlock
.Установив BoundedCapacity
, я гарантирую, что цикл отправки будет ждать, если во входной очереди spamBlock
будет слишком много необработанных сообщений.
ActionBlock
также может обрабатывать асинхронные методы.Предполагая, что GetTasksAsync
возвращает Task<Result[]>
, мы можем использовать:
var spamBlock=new TransformManyBlock<Line,Result>(
y=>GetTasksAsync(y.LineCode),
options);