Поэтому желательно, чтобы все документы были распараллелены, как если бы они принадлежали в одной последовательности, которая будет обрабатываться с одной настраиваемой степенью параллелизма от начала до конца sh.
Я обычно рекомендую использовать примитив самого высокого уровня, который возможен. В вашем случае, поскольку у вас есть разнородные действия с разными типами результатов, и вы также хотите иметь одну степень параллелизма, это ограничивает ваши параметры.
PLINQ - это вариант, хотя вам необходимо объединить входные данные и типы результатов. Что-то вроде:
int[] pdfIDs = new[] { 1, 2, 3 };
int[] xlsIDs = new[] { 11, 12, 13, 14 };
int[] docIDs = new[] { 21, 22 };
var inputs = pdfIDs.Select(id => (Type: "pdf", Id: id))
.Concat(xlsIDs.Select(id => (Type: "xls", Id: id)))
.Concat(docIDs.Select(id => (Type: "doc", Id: id)));
var process = inputs.AsParallel()
.WithDegreeOfParallelism(3)
.Select(x =>
{
switch (x.Type)
{
case "pdf": return (x.Type, File: (object) CreatePdfFile(x.Id));
case "xls": return (x.Type, File: (object) CreateXlsFile(x.Id));
case "doc": return (x.Type, File: (object) CreateDocFile(x.Id));
default: throw new InvalidOperationException($"Unknown type {x.Type}");
}
});
var results = process.ToList();
PdfFile[] pdfFiles = results.Where(x => x.Type == "pdf").Select(x => (PdfFile) x.File).ToArray();
XlsFile[] xlsFiles = results.Where(x => x.Type == "xls").Select(x => (XlsFile)x.File).ToArray();
DocFile[] odsFiles = results.Where(x => x.Type == "doc").Select(x => (DocFile)x.File).ToArray();
Или что-то в этом роде с лучшей безопасностью типов и меньшим количеством магических c строк. Перечисление и Choice
с некоторыми выражениями-переключателями сделают это лучше. :)
В качестве альтернативы, Parallel
будет работать хорошо. В этом случае, возможно, Parallel.Invoke
, где отдельные действия ответственны за сохранение своих собственных результатов в поточно-ориентированной коллекции:
int[] pdfIDs = new[] { 1, 2, 3 };
int[] xlsIDs = new[] { 11, 12, 13, 14 };
int[] docIDs = new[] { 21, 22 };
var pdfFileResults = new ConcurrentDictionary<int, PdfFile>();
var xlsFileResults = new ConcurrentDictionary<int, XlsFile>();
var docFileResults = new ConcurrentDictionary<int, DocFile>();
var pdfActions = pdfIDs.Select(id => (Action) (() => pdfFileResults.TryAdd(id, CreatePdfFile(id))));
var xlsActions = xlsIDs.Select(id => (Action) (() => xlsFileResults.TryAdd(id, CreateXlsFile(id))));
var docActions = docIDs.Select(id => (Action) (() => docFileResults.TryAdd(id, CreateDocFile(id))));
Parallel.Invoke(new ParallelOptions { MaxDegreeOfParallelism = 3 },
pdfActions.Concat(xlsActions).Concat(docActions).ToArray());
PdfFile[] pdfFiles = pdfFileResults.Values.ToArray();
XlsFile[] xlsFiles = xlsFileResults.Values.ToArray();
DocFile[] odsFiles = docFileResults.Values.ToArray();
Подход PLINQ - из-за его разделения - имеет тенденцию разделять работать между различными типами файлов. Подход Parallel.Invoke
имеет тенденцию проходить вниз по массиву действий по одному блоку за раз. Не уверен, что вы предпочтете.
Наконец, есть подход параллелизма на основе задач. Я вообще не рекомендую это из-за его сложности; его реальный сценарий использования - сценарий ios, где каждая задача может создавать больше задач, а не сценарий ios, подобный этому, где общее количество задач известно заранее. Поэтому я не рекомендую этот, но он интересен для полноты:
int[] pdfIDs = new[] { 1, 2, 3 };
int[] xlsIDs = new[] { 11, 12, 13, 14 };
int[] docIDs = new[] { 21, 22 };
var scheduler = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Default, maxConcurrencyLevel: 3).ConcurrentScheduler;
var factory = new TaskFactory(scheduler);
var pdfTasks = pdfIDs.Select(id => factory.StartNew(() => CreatePdfFile(id))).ToList();
var xlsTasks = xlsIDs.Select(id => factory.StartNew(() => CreateXlsFile(id))).ToList();
var docTasks = docIDs.Select(id => factory.StartNew(() => CreateDocFile(id))).ToList();
Task.WaitAll(pdfTasks.Cast<Task>().Concat(xlsTasks).Concat(docTasks).ToArray());
PdfFile[] pdfFiles = pdfTasks.Select(x => x.Result).ToArray();
XlsFile[] xlsFiles = xlsTasks.Select(x => x.Result).ToArray();
DocFile[] odsFiles = docTasks.Select(x => x.Result).ToArray();
Поскольку это все синхронные задачи, я бы использовал ConcurrentExclusiveSchedulerPair.ConcurrentScheduler
вместо SemaphoreSlim
. Это обычный шаблон для регулирования параллельного кода на основе задач.
Параллельный подход на основе задач имеет выполнение, аналогичное Parallel.Invoke
; поскольку все задачи ставятся в очередь в планировщике по группам по типу, они, как правило, выполняются.
В качестве заключительного замечания я должен вставить плагин для моей книги ; Я искренне думаю, что вам понравится. Мой блог посвящен асинхронности; моя книга также описывает параллелизм.