Запросы без результатов

89

PLINQ включает полезное средство в расширяющем методе ForAll. Использованный с объектом ParallelQuery (который, как известно, возвращается методом AsParallel), ForAll выполняет System.Action на каждом элементе в последовательности. В одном из рассмотренных ранее примеров ищутся все названия машин, содержащие букву "s". Для фильтрации соответствующих имен использовалась конструкция where, а выбранные имена добавлялись к результирующей коллекции IEnumerable<string>. Затем результаты перечисляются в цикле foreach с выводом их на консоль посредством Console.WriteLine.

С помощью метода ForAll можно сделать то же самое, но намного элегантнее. Рассмотрим код ниже:

string[] cars = { "Nissan", "Aston Martin", "Chevrolet", "Alfa Romeo", "Chrysler", "Dodge", "BMW",
                              "Ferrari", "Audi", "Bentley", "Ford", "Lexus", "Mercedes", "Toyota", "Volvo", "Subaru", "Жигули :)"};

            // Запрос Parallel LINQ
            cars.AsParallel()
                .Where(p => p.Contains("s"))
                .ForAll(p => Console.WriteLine("Название: " + p));

Здесь по-прежнему используется метод Where для фильтрации последовательности, но вместо сбора результатов имена выводятся на консоль непосредственно, с применением лямбда-выражения, переданного методу ForAll. Чтобы понять работу этого средства, может потребоваться некоторое время. В конце концов, все прочие примеры, приведенные ранее, функционировали иначе. Однако метод ForAll стоит знать.

Ниже показаны результаты запуска этого кода:

Использование метода ForAll

Внутри объекта Action, передаваемого методу ForAll, можно делать довольно много всего за исключением возврата результата. Можно даже фильтровать данные без помощи конструкции Where:

string[] cars = { "Nissan", "Aston Martin", "Chevrolet", "Alfa Romeo", "Chrysler", "Dodge", "BMW",
                              "Ferrari", "Audi", "Bentley", "Ford", "Lexus", "Mercedes", "Toyota", "Volvo", "Subaru", "Жигули :)"};

            int count = 0;

            cars.AsParallel()
                .ForAll(p =>
                {
                    if (p.Contains('s'))
                        System.Threading.Interlocked.Increment(ref count);
                });
            Console.WriteLine("Совпадений: " + count);

В приведенном коде метод ForAll использовался для выполнения действия над каждым элементом в последовательности данных. Для каждого элемента проверяется наличие в имени буквы "s" и увеличивается значение счетчика, если она там есть. Результат запуска кода выглядит следующим образом:

Фильтрация данных без конструкции Where

Не забывайте, что метод ForAll является частью PLINQ, а это значит, что указываемое в объекте Action действие выполняется в разделах последовательности данных параллельно. Это дает выигрыш в производительности за счет параллельного выполнения, но может привести к проблемам с разделяемыми данными, такими как значение int, используемое в качестве счетчика совпадений. Для обеспечения точности подсчета применяется класс Interlocked из пространства имен System.Threading. Этот прием называется синхронизацией, и представляет собой расширенную технику параллельного программирования.

Пройди тесты
Лучший чат для C# программистов