// Использовать объекты типа ParallelLoopResult и ParallelLoopState, а также
// метод Break() вместе с методом For() для параллельного выполнения цикла.
using System;
using System.Threading.Tasks;
class DemoParallelForWithLoopResult {
static int[] data;
// Метод, служащий в качестве тела параллельно выполняемого цикла.
//Операторы этого цикла просто расходуют время ЦП для целей демонстрации,
static void MyTransform(int i, ParallelLoopState pis) {
// Прервать цикл при обнаружении отрицательного значения,
if(data[i] < 0) pis.Break();
data[i] = data[i] / 10;
if(data[i] < 1000) data[i] = 0;
if(data[i] > 1000 & data[i] < 2000) data[i] = 100;
if(data[i] > 2000 & data[i] < 3000) data[i] = 200;
if(data[i] > 3000) data[i] = 300;
}
static void Main() {
Console.WriteLine("Основной поток запущен.");
data = new int[100000000];
// Инициализировать данные.
for(int i=0; i < data.Length; i++) data[i] = i;
// Поместить отрицательное значение в массив data, data[1000] = -10;
// Параллельный вариант инициализации массива в цикле.
ParallelLoopResult loopResult = Parallel.For(0, data.Length, MyTransform);
// Проверить, завершился ли цикл,
if(!loopResult.IsCompleted)
Console.WriteLine("\nЦикл завершился преждевременно из-за того, " +
"что обнаружено отрицательное значение" +
"на шаге цикла номер " +
loopResult.LowestBreakIteration + ".\n");
Console.WriteLine("Основной поток завершен.");
}
}
Выполнение этой программы может привести, например, к следующему результату.
Основной поток запущен.
Цикл завершился преждевременно из-за того, что обнаружено отрицательное значение на шаге цикла номер 1000
Основной поток завершен.
Как следует из приведенного выше результата, цикл преобразования данных преждевременно завершается после 1000 шагов. Дело в том, что метод Break()
вызывается внутри метода MyTransform()
при обнаружении в массиве данных отрицательного значения.
Помимо двух описанных выше форм метода For(
) существует и ряд других его форм. В одних из этих форм допускается указывать различные дополнительные параметры, а в других — использовать параметры типа long
вместо int
для пошагового выполнения цикла. Имеются также формы метода For()
, предоставляющие такие дополнительные преимущества, как, например, возможность указывать метод, вызываемый по завершении потока каждого цикла.
И еще одно, последнее замечание: если требуется остановить цикл, параллельно выполняемый методом For()
, не обращая особого внимания на любые шаги цикла, которые еще могут быть в нем выполнены, то для этой цели лучше воспользоваться методом Stop()
, чем методом Break()
.
Применение метода ForEach()
Используя метод ForEach()
, можно создать распараллеливаемый вариант цикла f oreach
. Существует несколько форм метода ForEach()
. Ниже приведена простейшая форма его объявления:
public static ParallelLoopResult
ForEach(IEnumerable source,
Action body)
где source обозначает коллекцию данных, обрабатываемых в цикле, a body — метод, который будет выполняться на каждом шаге цикла. Как пояснялось ранее в этой книге, во всех массивах, коллекциях (описываемых в главе 25) и других источниках данных поддерживается интерфейс IEnumerable
. Метод, передаваемый через параметр body, принимает в качестве своего аргумента значение или ссылку на каждый обрабатываемый в цикле элемент массива, но не его индекс. А в итоге возвращаются сведения о состоянии цикла.
Аналогично методу For()
, параллельное выполнение цикла методом ForEach()
можно остановить, вызвав метод Break()
для экземпляра объекта типа ParallelLoopState
, передаваемого через параметр body
, при условии, что используется приведенная ниже форма метода ForEach()
.
public static ParallelLoopResult
ForEach(IEnumerable source,
Action body)
В приведенном ниже примере программы демонстрируется применение метода ForEach()
на практике. Как и прежде, в данном примере создается крупный массив целых значений. А отличается данный пример от предыдущих тем, что метод, выполняющийся на каждом шаге цикла, просто выводит на консоль значения из массива. Как правило, метод WriteLine()
в распараллеливаемом цикле не применяется, потому что ввод-вывод на консоль осуществляется настолько медленно, что цикл оказывается полностью привязанным к вводу-выводу. Но в данном примере метод WriteLine()
применяется исключительно в целях демонстрации возможностей метода ForEach()
. При обнаружении отрицательного значения выполнение цикла прерывается вызовом метода Break()
. Несмотря на то что метод Break()
вызывается в одной задаче, другая задача может по-прежнему выполняться в течение нескольких шагов цикла, прежде чем он будет прерван, хотя это зависит от конкретных условий работы среды выполнения.
Читать дальше