Чтение онлайн

ЖАНРЫ

C# 4.0 полное руководство - 2011

Шилдт Герберт

Шрифт:

Прерывание цикла, параллельно выполняемого методом For , нередко оказывается полезным при поиске данных. Так, если искомое значение найдено, то продолжать выполнение цикла нет никакой надобности. Прерывание цикла может оказаться полезным и в том случае, если во время очередной операции встретились недостоверные данные.

В приведенном ниже примере программы демонстрируется применение метода Break для прерывания цикла, параллельно выполняемого методом For . Это вариант предыдущего примера, переработанный таким образом, чтобы метод MyTransform принимал теперь объект типа ParallelLoopState в качестве своего параметра,

а метод
Break вызывался при обнаружении отрицательного значения в массиве данных. Отрицательное значение, по которому прерывается выполнение цикла, вводится в массив data внутри метода Main . Далее проверяется состояние завершения цикла преобразования данных. Свойство IsCompleted будет содержать логическое значение false, поскольку в массиве data обнаруживается отрицательное значение. При этом на экран выводится номер шага, на котором цикл был прерван. (В этой программе исключены все избыточные циклы, применявшиеся в ее предыдущей версии, а оставлены только самые эффективные из них: последовательно выполняемый цикл инициализации и параллельно выполняемый цикл преобразования.)

// Использовать объекты типа 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 передается значение элемента массива // данных, а не индекс этого элемента.

Поделиться с друзьями: