Подтвердить что ты не робот

Parallel.ForEach Упорядоченное исполнение

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

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

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

Я хочу этот порядок:

a1, a2, a3, a4...

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

4b9b3361

Ответ 1

Если вы используете 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

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

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]);
                 });

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

Ответ 3

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

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

Ответ 4

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

Заказ PLINQ ForAll

Ответ 5

Для всех, кто сталкивается с этим вопросом - если вы перебираете массив или список (а не 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;
});

Ответ 6

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

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

Это общее утверждение, так как вопрос не является точным.