Я бы хотел обрабатывать некоторые элементы параллельно. Эта обработка независима (порядок не имеет значения) и возвращает результат. Затем эти выходы должны быть ретранслированы обратно по порядку как можно быстрее.
То есть метод должен вести себя эквивалентно этому (кроме вызова Process
параллельно):
IEnumerable<T> OrderedParallelImmediateSelect<T> (IEnumerable<object> source)
{
foreach (var input in source) {
var result = Process (input);
yield return result;
}
}
Соответственно, требуется попытаться обработать элементы по порядку. Так как это (конечно) не гарантировано завершить по порядку, сборщик результатов должен обязательно дождаться отложенных результатов.
Как только приходит следующий результат в заказе, он должен быть немедленно возвращен. Мы не можем дождаться обработки всего ввода перед сортировкой результатов.
Это пример того, как это может выглядеть:
begin 0
begin 1 <-- we start processing in increasing order
begin 2
complete 1 <-- 1 is complete but we are still waiting for 0
begin 3
complete 0 <-- 0 is complete, so we can return it and 1, too
return 0
return 1
begin 4
begin 5
complete 4 <-- 2 and 3 are missing before we may return this
complete 2 <-- 2 is done, 4 must keep waiting
return 2
begin 6
complete 3 <-- 3 and 4 can now be returned
return 3
return 4
Если это вообще возможно, я хотел бы выполнить обработку в обычном пуле потоков.
Является ли этот сценарий решением для .NET? Я создал собственное решение, но предпочел бы использовать что-то более простое.
Мне известно о многих похожих вопросах, но, похоже, все они либо позволяют дождаться завершения обработки всех элементов, либо не гарантируют заказанные результаты.
Вот попытка, которая, к сожалению, не работает. Замена IEnumerable
на ParallelQuery
не имела никакого эффекта.
int Process (int item)
{
Console.WriteLine ($"+ {item}");
Thread.Sleep (new Random (item).Next (100, 1000));
Console.WriteLine ($"- {item}");
return item;
}
void Output (IEnumerable<int> items)
{
foreach (var it in items) {
Console.WriteLine ($"=> {it}");
}
}
IEnumerable<int> OrderedParallelImmediateSelect (IEnumerable<int> source)
{
// This processes in parallel but does not return the results immediately
return source.AsParallel ().AsOrdered ().Select (Process);
}
var input = Enumerable.Range (0, 20);
Output (OrderedParallelImmediateSelect (input));
Вывод:
+ 0 +1 +3 +2 +4 +5 +6 +7 +9 +10 +11 +8 -1 +12 -3 +13 -5 +14 -7 +15 -9 +16 -11 + 17 -14 +18 -16 +19 -0 -18 -2 -4 -6 -8 -13 -10 -15 -17 -12 -19 => 0 => 1 => 2 => 3 => 4 => 5 => 6 => 7 => 8 => 9 => 10 => 11 => 12 => 13 => 14 => 15 => 16 => 17 => 18 => 19