Заказанный PLINQ ForAll

документация msdn о сохранение заказа в PLINQ говорится о ForAll().

  • результат, когда исходная последовательность приказал: выполняет недетерминированно параллельно
  • результат, когда исходная последовательность ненумерованный: выполняет недетерминированно параллельно

означает ли это, что упорядоченное выполнение ForAll метод никогда не гарантируется?

Я не использовал PLINQ раньше, но следующее код обзор вопрос казалось подходящим использованием для этого. В конце ответа я пишу:--9-->

Events.AsParallel().AsOrdered().ForAll( eventItem =>
{
    ...
} );    

после прочтения документации я верю AsOrdered() ничего не изменит?
я также подозреваю, что предыдущий запрос не может заменить простой for петля, где порядок важен?
вероятно, параллельные вызовы StringBuilder также будет происходить, в результате неправильный выход?

4 ответов


сохранение заказа обычно применяется только к результаты - т. е. вход может быть обработки в любом порядке, но вернулся в исходном порядке.

As ForAll ничего не возвращает, на самом деле это не имеет никакого эффекта, о котором я знаю.

единственный способ сделать заказ применить к обработка было бы закончить деталь 0 перед обрабатывать деталь 1, перед обрабатывать деталь 2 etc... в какой момент у вас нет параллелизма.


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

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

public static void ForAllInApproximateOrder<TSource>(this ParallelQuery<TSource> source, Action<TSource> action) {

    Partitioner.Create( source )
               .AsParallel()
               .AsOrdered()
               .ForAll( e => action( e ) );

}

это можно использовать следующим образом:

orderedElements.AsParallel()
               .ForAllInApproximateOrder( e => DoSomething( e ) );

следует отметить, что вышеуказанный метод расширения использует PLINQ ForAll, а не Parallel.ForEach и поэтому наследует модель резьбы, используемую interally PLINQ (которая отличается от используемой Parallel.ForEach -- менее агрессивный по умолчанию в моем опыте). Аналогичный метод расширения с помощью Parallel.ForEach ниже.

public static void ForEachInApproximateOrder<TSource>(this ParallelQuery<TSource> source, Action<TSource> action) {

    source = Partitioner.Create( source )
                        .AsParallel()
                        .AsOrdered();

    Parallel.ForEach( source , e => action( e ) );

}

это можно использовать следующим образом:

orderedElements.AsParallel()
               .ForEachInApproximateOrder( e => DoSomething( e ) );

нет надо цепьAsOrdered() к вашему запросу при использовании любого из вышеуказанных методов расширения он все равно вызывается внутренне.

Я нашел эти методы полезными при обработке элементов, которые имеют крупнозернистое значение. Может быть полезно, например, обрабатывать записи, начиная с самых старых и работая в направлении новейших. Во многих случаях точный порядок записей не требуется - поскольку старые записи обычно обрабатываются до новых записей. Аналогично, записи низкоприоритетные/med / high priority уровни могут быть обработаны таким образом, что записи с высоким приоритетом будут обработаны до записей с более низким приоритетом для большинства случаев, с крайними случаями не далеко позади.


AsOrdered() ничего не изменит - если вы хотите обеспечить порядок в результате параллельного запроса, вы можете просто использовать foreach() ForAll() тут воспользуйтесь параллелизмом, это означает выполнение побочного эффекта для более чем одного элемента в коллекции за раз. На самом деле порядок применяется только к результатам запроса (порядок элементов в коллекции результатов), но это не имеет ничего общего с ForAll() С ForAll() не влияет на заказ на все.

в PLINQ, цель состоит в том, чтобы максимизировать производительность при сохранении корректность. Запрос должен выполняться как как можно быстрее, но все еще производят правильный результат. В некоторых случаях, корректность требует порядок исходной последовательности для сохранения

отметим, что ForAll() не преобразует коллекцию (это не я.e проецирование в новую коллекцию), это исключительно для выполнения побочных эффектов на результаты PLINQ запрос.


означает ли это, что упорядоченное выполнение метода ForAll никогда не гарантируется?

Yes-заказ не гарантируется.

распараллеливание означает, что работа распределяется по разным потокам, а их отдельные выходы затем объединяются.

Если вам нужно заказать вывод, не используйте PLinq-или добавьте какой-то более поздний шаг, чтобы вернуть заказ.


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