C# 4.0 полное руководство - 2011
Шрифт:
Прерывание цикла, параллельно выполняемого методом For , нередко оказывается полезным при поиске данных. Так, если искомое значение найдено, то продолжать выполнение цикла нет никакой надобности. Прерывание цикла может оказаться полезным и в том случае, если во время очередной операции встретились недостоверные данные.
В приведенном ниже примере программы демонстрируется применение метода Break для прерывания цикла, параллельно выполняемого методом For . Это вариант предыдущего примера, переработанный таким образом, чтобы метод MyTransform принимал теперь объект типа ParallelLoopState в качестве своего параметра,
// Использовать объекты типа 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("\пЦикл завершился преждевременно из-за того, " +
"что обнаружено отрицательное значение\п" +
"на шаге цикла номер " +
loopResult.LowestBreaklteration + ".\n");
Console.WriteLine("Основной поток завершен.");
}
}
Выполнение этой программы может привести, например, к следующему результату.
Основной поток запущен.
Цикл завершился преждевременно из-за того, что обнаружено отрицательное значение на шаге цикла номер 1000
Основной поток завершен.
Как следует из приведенного выше результата, цикл преобразования данных преждевременно завершается после 1000 шагов. Дело в том, что метод Break вызывается внутри метода MyTransform при обнаружении в массиве данных отрицательного значения.
Помимо двух описанных выше форм метода For существует и ряд других его форм. В одних из этих форм допускается указывать различные дополнительные параметры, а в других — использовать параметры типа long вместо int для пошагового выполнения цикла. Имеются также формы метода For , предоставляющие такие дополнительные преимущества, как, например, возможность указывать метод, вызываемый по завершении потока каждого цикла.
И еще одно, последнее замечание: если требуется остановить цикл, параллельно выполняемый методом For , не обращая особого внимания на любые шаги цикла, которые еще могут быть в нем выполнены, то для этой цели лучше воспользоваться методом Stop , чем методом Break .
Применение метода ForEach
Используя метод ForEach , можно создать распараллеливаемый вариант цикла foreach. Существует несколько форм метода ForEach . Ниже приведена простейшая форма его объявления:
public static ParallelLoopResult
ForEach<TSource>(IEnumerable<TSource> source,
Action<TSource> body)
где source обозначает коллекцию данных, обрабатываемых в цикле, a body — метод, который будет выполняться на каждом шаге цикла. Как пояснялось ранее в этой книге, во всех массивах, коллекциях (описываемых в главе 25) и других источниках данных поддерживается интерфейс IEnumerable<T>. Метод, передаваемый через параметр body, принимает в качестве своего аргумента значение или ссылку на каждый обрабатываемый в цикле элемент массива, но не его индекс. А в итоге возвращаются сведения
о состоянии цикла.
Аналогично методу For , параллельное выполнение цикла методом ForEach можно остановить, вызвав метод Break для экземпляра объекта типа ParallelLoopState, передаваемого через параметр body, при условии, что используется приведенная ниже форма метода For Each .
public static ParallelLoopResult
ForEach<TSource>(IEnumerable<TSource> source,
ActiorKTSource, ParallelLoopState> body)
В приведенном ниже примере программы демонстрируется применение метода For Each на практике. Как и прежде, в данном примере создается крупный массив целых значений. А отличается данный пример от предыдущих тем, что метод, выполняющийся на каждом шаге цикла, просто выводит на консоль значения из массива. Как правило, метод WriteLine в распараллеливаемом цикле не применяется, потому что ввод-вывод на консоль осуществляется настолько медленно, что цикл оказывается полностью привязанным к вводу-выводу. Но в данном примере метод WriteLine применяется исключительно в целях демонстрации возможностей метода ForEach . При обнаружении отрицательного значения выполнение цикла прерывается вызовом метода Break . Несмотря на то что метод Break вызывается в одной задаче, другая задача может по-прежнему выполняться в течение нескольких шагов цикла, прежде чем он будет прерван, хотя это зависит от конкретных условий работы среды выполнения.
// Использовать объекты типа ParallelLoopResult и ParallelLoopState, а также // метод Break вместе с методом ForEachO для параллельного выполнения цикла.
using System;
using System.Threading.Tasks;
class DemoParallelForWithLoopResult { static int[] data;
// Метод, служащий в качестве тела параллельно выполняемого цикла.
// В данном примере переменной v передается значение элемента массива // данных, а не индекс этого элемента.