Parallel.ForEach Упорядоченное выполнение - PullRequest
18 голосов
/ 04 сентября 2010

Я пытаюсь выполнить параллельные функции в списке объектов, используя новую функцию C # 4.0 Parallel.ForEach. Это очень долгий процесс обслуживания. Я хотел бы, чтобы он выполнялся в порядке списка, чтобы я мог остановить и продолжить выполнение в предыдущем пункте. Как мне это сделать?

Вот пример. У меня есть список объектов: a1 to a100. Это текущий заказ:

a1, a51, a2, a52, a3, a53...

Я хочу этот заказ:

a1, a2, a3, a4...

Я в порядке с некоторыми объектами, которые работают не по порядку, но пока я могу найти точку в списке, где я могу сказать, что все объекты до этой точки были запущены. Я прочитал документацию по параллельному программированию csharp и ничего об этом не увидел. В классе ParallelOptions для этого нет настройки.

Ответы [ 6 ]

3 голосов
/ 05 сентября 2010

Если вы используете Parallel.Break для завершения цикла, тогда вы гарантированно, что все индексы ниже возвращаемого значения будут выполнены. Это примерно так близко, как вы можете получить. В данном примере используется For, но ForEach имеет аналогичные перегрузки.

int n = ...
var result = new double[n];

var loopResult = Parallel.For(0, n, (i, loopState) =>
{
   if (/* break condition is true */)
   {
      loopState.Break();
      return;
   }
   result[i] = DoWork(i);
});

if (!loopResult.IsCompleted && 
        loopResult.LowestBreakIteration.HasValue)
{
   Console.WriteLine("Loop encountered a break at {0}", 
                      loopResult.LowestBreakIteration.Value);
}

В цикле ForEach для каждого элемента в каждом разделе создается внутренний итерационный индекс. Выполнение происходит не по порядку, но после перерыва вы знаете, что все итерации, меньшие LowestBreakIteration, будут выполнены.

Взято из "Параллельное программирование с Microsoft .NET" http://parallelpatterns.codeplex.com/

Доступно в MSDN. См. http://msdn.microsoft.com/en-us/library/ff963552.aspx. Раздел «Ранний выход из цикла» охватывает этот сценарий.

Смотри также: http://msdn.microsoft.com/en-us/library/dd460721.aspx

2 голосов
/ 25 июня 2015

Сделайте что-то вроде этого:

int current = 0;
object lockCurrent = new object();

Parallel.For(0, list.Count, 
             new ParallelOptions { MaxDegreeOfParallelism = MaxThreads },
             (ii, loopState) => {
                    // So the way Parallel.For works is that it chunks the task list up with each thread getting a chunk to work on...
                    // e.g. [1-1,000], [1,001- 2,000], [2,001-3,000] etc...
                    // We have prioritized our job queue such that more important tasks come first. So we don't want the task list to be
                    // broken up, we want the task list to be run in roughly the same order we started with. So we ignore tha past in 
                    // loop variable and just increment our own counter.
                    int thisCurrent = 0;
                    lock (lockCurrent) {
                        thisCurrent = current;
                        current++;
                    }
                    dothework(list[thisCurrent]);
                 });

Вы можете видеть, как при выходе из параллельного цикла for вы узнаете последний элемент списка, который должен быть выполнен, при условии, что все потоки заканчиваются до прерывания,Я не большой поклонник PLINQ или LINQ.Честно говоря, я не понимаю, как написание LINQ / PLINQ приводит к удобству сопровождения исходного кода или удобочитаемости ... Parallel.For - гораздо лучшее решение.

1 голос
/ 05 января 2014

Для тех, кто ищет простое решение, я опубликовал 2 метода расширения (один с использованием PLINQ и один с использованием Parallel.ForEach) как часть ответа на следующий вопрос:

Заказано PLINQ ForAll

1 голос
/ 18 августа 2011

Для всех, кто сталкивается с этим вопросом - если вы зацикливаетесь на массиве или списке (а не на IEnumberable), вы можете использовать перегрузку Parallel.Foreach, которая дает индекс элемента для сохранения исходного порядка.

string[] MyArray; // array of stuff to do parallel tasks on 
string[] ProcessedArray = new string[MyArray.Length];
Parallel.ForEach(MyArray, (ArrayItem,loopstate,ArrayElementIndex) =>
{
    string ProcessedArrayItem = TaskToDo(ArrayItem);
    ProcessedArray[ArrayElementIndex] = ProcessedArrayItem;
});
1 голос
/ 05 сентября 2010

В качестве альтернативы вы можете записать, какой объект был запущен, а затем отфильтровать список при возобновлении исключения, чтобы исключить уже запущенные объекты.

Если это необходимо сохранить при перезапуске приложения, вы можете сохранить идентификаторы уже выполненных объектов (я предполагаю, что здесь объекты имеют некоторый уникальный идентификатор).

0 голосов
/ 25 марта 2016

Не уверен, что вопрос был изменен, так как мой комментарий кажется неправильным.
Здесь улучшены, в основном напоминают, что параллельные задания выполняются вне вашего порядка управления.
Печать 10 цифр может привести к получению 1,4,6,7,2,3,9,0.

Если вы хотите остановить свою программу и продолжить позже.
Подобные проблемы обычно приводят к пакетным рабочим нагрузкам.
И есть некоторые записи того, что было сделано.
Скажите, если вам нужно было проверить 10.000 номеров для простого или около того.
Вы можете зациклить в партии размером 100, и иметь простой log1, log2, log3
log1 = 0..99
log2 = 100..199
Обязательно установите маркер, чтобы узнать, было ли завершено пакетное задание.

Это общий вопрос, так как вопрос тоже не точный.

...