Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Базовые технологии платформы .NET.docx
Скачиваний:
15
Добавлен:
02.11.2018
Размер:
626.37 Кб
Скачать

Параллелизм при императивной обработке данных

Класс System.Threading.Tasks.Parallel позволяет распараллеливать циклы и последовательность блоков кода. Эта функциональность реализована как набор статических методов For(), ForEach() и Invoke().

Методы Parallel.For() и Parallel.ForEach() являются параллельными аналогами циклов for и foreach. Их использование корректно в случае независимости итераций цикла, то есть, если ни в одной итерации не используется результаты работы предыдущих итераций.

Существует несколько перегруженных вариантов метода Parallel.For(), однако любой из них подразумевает указание начального и конечного значения счётчика (тип int или long) и тела цикла в виде объекта делегата. В качестве примера использования Parallel.For() приведём метод, выполняющий перемножение двух квадратных матриц.

public void Multiply(int size, int[,] m1, int[,] m2, int[,] result)

{

Parallel.For(0, size, i =>

{

for (int j = 0; j < size; j++)

{

result[i, j] = 0;

for (int k = 0; k < size; k++)

{

result[i, j] += m1[i, k] * m2[k, j];

}

}

});

}

Метод Parallel.ForEach() имеет множество перегрузок. Простейший вариант предполагает указание коллекции, реализующей IEnumerable<T>, и объекта делегата Action<T>, описывающего тело цикла:

Parallel.ForEach(Directory.GetFiles(path, "*.jpg"), img => Process(img));

Статический метод Parallel.Invoke() позволяет распараллелить исполнение блоков операторов. Часто в приложениях существуют такие последовательности операторов, для которых не имеет значения порядок выполнения операторов внутри них. В таких случаях вместо последовательного выполнения операторов одного за другим, возможно их параллельное выполнение, позволяющее сократить время решения задачи. В базовом варианте Invoke() принимает параметр-список объектов делегата Action:

Parallel.Invoke(DoSomeWork, // DoSomeWork - это некий метод

DoAnotherWork, // DoAnotherWork - некий метод

() => Console.WriteLine("Working..."));

В заключение заметим, что каждый из методов For(), ForEach() и Invoke() может принимать аргумент типа ParallelOptions, используемый для настройки поведения метода.

Параллелизм при декларативной обработке данных

PLINQ (Parallel Language-Integrated Query) ‑ параллельная реализация LINQ, в которой запросы выполняются параллельно, используя все доступные ядра и процессоры. PLINQ полностью поддерживает все операторы запросов, имеющиеся в LINQ to Objects, и имеет минимальное влияние на существующую модель LINQ-операторов.

Рассмотрим простой пример использования PLINQ. Предположим, что имеется медленный метод, который проверяет делимость целого числа на 5:

public static bool IsDivisibleBy5(int x)

{

Thread.Sleep(100);

return x % 5 == 0;

}

Подсчитаем количество чисел, которые делятся на 5, в заданном интервале при помощи обычного LINQ:

var numbers = Enumerable.Range(1, 100);

int count = numbers.Where(IsDivisibleBy5).Count();

Console.WriteLine(count);

Чтобы распараллелить этот запрос средствами PLINQ, достаточно применить к источнику данных (numbers) метод расширения AsParallel():

var numbers = Enumerable.Range(1, 100);

int count = numbers.AsParallel().Where(IsDivisibleBy5).Count();

Console.WriteLine(count);

Почему работоспособен приведённый выше код? В пространстве имён System.Linq содержится статический класс ParallelEnumerable. Он имеет набор методов расширения, аналогичный набору класса Enumerable, но расширяющих класс ParallelQuery<T>. Метод расширения AsParallel() просто «конвертирует» коллекцию IEnumerable<T> в объект ParallelQuery<T>.

Кроме AsParallel(), класс ParallelEnumerable содержит ещё несколько особых методов:

  • AsSequential() ‑ конвертирует объект ParallelQuery<T> в коллекцию IEnumerable<T> так, что все запросы выполняются последовательно;

  • AsOrdered() ‑ при параллельной обработке заставляет сохранять в ParallelQuery<T> порядок элементов;

  • AsUnordered() ‑ при параллельной обработке позволяет игнорировать в ParallelQuery<T> порядок элементов;

  • WithCancellation() ‑ устанавливает для ParallelQuery<T> указанное значение токена отмены;

  • WithDegreeOfParallelism() ‑ устанавливает для ParallelQuery<T> целочисленное значение степени параллелизма (число ядер процессоров);

  • WithExecutionMode() ‑ задаёт опции выполнения параллельных запросов в виде перечисления ParallelExecutionMode.

int count = numbers.AsParallel().

AsOrdered().

WithExecutionMode(ParallelExecutionMode.ForceParallelism).

Where(IsDivisibleBy5).Count();