Основы параллельного программирования с использованием Visual Studio 2010

Параллелизм данных

Разбить на страницы
Показывать лекцию целиком

Класс Parallel

Одним из новых классов в .NET Framework 4.0 - является класс System.Threading.Tasks.Parallel. Этот класс поддерживает набор методов, которые позволяют выполнять итерации по коллекции данных в параллельном режиме. Одними из таких методов являются Parallel.For() и Parallel.ForEach(), Parallel.Invoke():

  • Parallel.Invoke: выполняет параллельно массив делегатов;
  • Parallel.For: параллельный эквивалент цикла for;
  • Parallel.ForEach: параллельный эквивалент цикла foreach.
  • Все три метода блокируют управление до окончания выполнения всех действий. При возникновении необработанного исключения, в каком-то из потоков, оставшиеся рабочие потоки прекращают выполнение, и вызывается исключение - AggregationException. Основные методы класса Parallel представлены в Табл. 9.1.

    Основные методы класса Parallel
    Имя Описание
    For(Int32, Int32, Action) Выполняет цикл for, в котором итерации могут выполняться параллельно.
    For(Int32, Int32, Action<Int32,ParallelLoopState>) Выполняет цикл for, обеспечивая возможность параллельного выполнения итераций, а также контроля состояния цикла и управления этим состоянием.
    ForEach(IEnumerable, Action) Выполняет операцию foreach (For Each в Visual Basic) для объекта IEnumerable, обеспечивая возможность параллельного выполнения итераций.
    ForEach(IEnumerable, Action<Int32,ParallelLoopState>) Выполняет операцию foreach (For Each в Visual Basic) для объекта IEnumerable, обеспечивая возможность параллельного выполнения итераций, а также контроля состояния цикла и управления этим состоянием.
    Invoke(Action[]) Выполняет все предоставленные действия, в том числе параллельно.
    Invoke(ParallelOptions, Action[]) Выполняет каждое из указанных действий по возможности в параллельном режиме, если операция не отменена пользователем.

    Parallel.Invoke

    Метод Parallel.Invoke() позволяет выполнять один или несколько методов, указываемых в виде его аргументов. При использовании данного метода отпадает необходимость использования метода Wait(), т.к. Invoke() сначала инициирует выполнение, а затем ожидает завершения всех передаваемых ему методов. Простая версия этого метода описана ниже:

    public static void Invoke (params Action[] actions);

    Существует также перегруженная версия метода Parallel.Invoke(), которая принимает объект класса ParallelOptions:

    public static void Invoke (ParallelOptions options, params Action[] actions);

    где optinons - объект класса ParallelOptions, с помощью которого можно добавлять маркеры (token) отмены, ограничить максимальное количество рабочих потоков или указать свой планировщик задач (custom task scheduler). Использование маркеров отмены полезно, когда число одновременно выполняемых задач превосходит количество ядер процессора: при отмене все делегаты, выполнение которых еще не было начато, будут отменены. Однако все делегаты, выполнение которых уже начато, продолжат до завершения.

    Пример работы метода Invoke() представлен ниже:

    static void Hello()
            {
                Console.WriteLine("Hello");
            }
    
            static void World()
            {
                Console.WriteLine("World!");
            }
    
            static void WorldHello()
            {
                Console.WriteLine("World Hello!");
            }
    
            static void Main(string[] args)
            {
                Parallel.Invoke(Hello, World, WorldHello);
                Console.ReadLine();
       }
    (рис 9.1) Результат работы программы с использованием метода Invoke()

    Parallel.For

    Метод Parallel.For() аналогичен оператору for, за исключением того что все итерации выполняются в отдельных потоках, при этом порядок выполнения итераций не определен. Конструкция данного метода представлена ниже:

    Parallel.For (Int32, Int32, Action)

    где первый параметр (Int32) - начальный индекс (включительно) цикла, второй параметр (Int32) - это конечный индекс (не включительно) цикла, третий параметр (Action) передаётся делегат, который вызывается один раз за итерацию. Возвращаемым типом у метода Parallel.For() является структура ParallelLoopResult, в которой содержатся сведения о выполненной части цикла.

    Пример использование параллельного цикла Parallel.For() представлен ниже:

    Parallel.For(0, 10, i =>
            {
                Console.WriteLine("{0}, Задача: {1}, Поток: {2}", i,
                   Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(10);
    
            });        Console.ReadLine();
    (рис 9.2) Результат работы программы с использованием метода Parallel.For()

    В одной из перегрузок этого метода Parallel.For() есть третий параметр Action<int, ParallelLoopState>. С помощью такой перегрузки метода, можно оказывать влияние на выполнение цикла с помощью методов Break() и Stop() объекта ParallelLoopState:

  • Stop() - применяется в алгоритмах поиска, где после нахождения результата, выполнять другие итерации не требуется;
  • Break() - используется для передачи циклу информации, что другие итерации после текущей итерации выполнять не требуется.
  • Пример кода с использованием объекта ParallelLoopState представлен ниже:

    Parallel.For(0, 100, (i,state) =>
            {
                Console.WriteLine("{0}, Задача: {1}, Поток: {2}", i,
                   Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(10);
                if (i == 55) state.Break();
    
            });
    (рис 9.3) Результат работы программы с использованием метода Parallel.For() и объекта ParallelLoopState

    Parallel.ForEach

    Метод Parallel.ForEach(), позволяет создавать распараллеленный вариант цикла foreach. Существует несколько форм метода Parallel.ForEach(). Одна из простых форм объявление данного метода приведена ниже:

    ForEach<TSource>(IEnumerable<TSource> source, Action<TSource> body);

    где source - обозначает коллекцию данных, обрабатываемых в цикле, body - метод, который будет выполняться на каждом шаге цикла.

    Пример реализации метода Parallel.ForEach(), представлен ниже:

    int[] data = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
    
                
                    Parallel.ForEach<int>(data, l =>
                    {
                        Console.WriteLine(l);
                    });
           
                Console.ReadLine();
    (рис 9.4) Результат работы программы с использованием метода Parallel.Foreach()

    Параллельное выполнение цикла методом Parallel.ForEach() также как и метод Parallel.For(), можно остановить, вызвав метод Break() для экземпляра объекта ParallelLoopState, передаваемого через параметр body, форма метода Parallel.Foreach() приведена ниже:

    ForEach<TSource>(IEnumerable<TSource> source,Action<TSource, ParallelLoopState> body);

    Пример реализации метода Parallel.ForEach(), с использованием объекта ParallelLoopState представлен ниже:

    char[] data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
    
      ParallelLoopResult result =
                    Parallel.ForEach(data, (l, state) =>
                    {
                        
                        Console.WriteLine(l);
                      if (l == 'e') state.Break();
                       
                    });
    (рис 9.5) Результат работы программы с использованием метода Parallel.Foreach() и объекта ParallelLoopState

    Для того что бы получить индекс параллельной версии цикла foreach используется следующая форма метода Parallel.Foreach():

    public static ParallelLoopResult ForEach<TSource> (IEnumerable<TSource> source, Action<TSource,ParallelLoopState,long> body

    где long - индекс цикла.

    Пример реализации метода Parallel.ForEach(), с использованием индексов цикла представлен ниже:

    char[] data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
    
                ParallelLoopResult result =
                    Parallel.ForEach(data, (l, state,i) =>
                    {
                        Console.WriteLine(l+" "+i);
                    });
    (рис 9.6) Результат работы программы с использованием метода Parallel.Foreach() и индексов цикла
    Страницы:

    Класс Parallel

    Одним из новых классов в .NET Framework 4.0 - является класс System.Threading.Tasks.Parallel. Этот класс поддерживает набор методов, которые позволяют выполнять итерации по коллекции данных в параллельном режиме. Одними из таких методов являются Parallel.For() и Parallel.ForEach(), Parallel.Invoke():

  • Parallel.Invoke: выполняет параллельно массив делегатов;
  • Parallel.For: параллельный эквивалент цикла for;
  • Parallel.ForEach: параллельный эквивалент цикла foreach.
  • Все три метода блокируют управление до окончания выполнения всех действий. При возникновении необработанного исключения, в каком-то из потоков, оставшиеся рабочие потоки прекращают выполнение, и вызывается исключение - AggregationException. Основные методы класса Parallel представлены в Табл. 9.1.

    Основные методы класса Parallel
    Имя Описание
    For(Int32, Int32, Action) Выполняет цикл for, в котором итерации могут выполняться параллельно.
    For(Int32, Int32, Action<Int32,ParallelLoopState>) Выполняет цикл for, обеспечивая возможность параллельного выполнения итераций, а также контроля состояния цикла и управления этим состоянием.
    ForEach(IEnumerable, Action) Выполняет операцию foreach (For Each в Visual Basic) для объекта IEnumerable, обеспечивая возможность параллельного выполнения итераций.
    ForEach(IEnumerable, Action<Int32,ParallelLoopState>) Выполняет операцию foreach (For Each в Visual Basic) для объекта IEnumerable, обеспечивая возможность параллельного выполнения итераций, а также контроля состояния цикла и управления этим состоянием.
    Invoke(Action[]) Выполняет все предоставленные действия, в том числе параллельно.
    Invoke(ParallelOptions, Action[]) Выполняет каждое из указанных действий по возможности в параллельном режиме, если операция не отменена пользователем.

    Parallel.Invoke

    Метод Parallel.Invoke() позволяет выполнять один или несколько методов, указываемых в виде его аргументов. При использовании данного метода отпадает необходимость использования метода Wait(), т.к. Invoke() сначала инициирует выполнение, а затем ожидает завершения всех передаваемых ему методов. Простая версия этого метода описана ниже:

    public static void Invoke (params Action[] actions);

    Существует также перегруженная версия метода Parallel.Invoke(), которая принимает объект класса ParallelOptions:

    public static void Invoke (ParallelOptions options, params Action[] actions);

    где optinons - объект класса ParallelOptions, с помощью которого можно добавлять маркеры (token) отмены, ограничить максимальное количество рабочих потоков или указать свой планировщик задач (custom task scheduler). Использование маркеров отмены полезно, когда число одновременно выполняемых задач превосходит количество ядер процессора: при отмене все делегаты, выполнение которых еще не было начато, будут отменены. Однако все делегаты, выполнение которых уже начато, продолжат до завершения.

    Пример работы метода Invoke() представлен ниже:

    static void Hello()
            {
                Console.WriteLine("Hello");
            }
    
            static void World()
            {
                Console.WriteLine("World!");
            }
    
            static void WorldHello()
            {
                Console.WriteLine("World Hello!");
            }
    
            static void Main(string[] args)
            {
                Parallel.Invoke(Hello, World, WorldHello);
                Console.ReadLine();
       }
    (рис 9.1) Результат работы программы с использованием метода Invoke()

    Parallel.For

    Метод Parallel.For() аналогичен оператору for, за исключением того что все итерации выполняются в отдельных потоках, при этом порядок выполнения итераций не определен. Конструкция данного метода представлена ниже:

    Parallel.For (Int32, Int32, Action)

    где первый параметр (Int32) - начальный индекс (включительно) цикла, второй параметр (Int32) - это конечный индекс (не включительно) цикла, третий параметр (Action) передаётся делегат, который вызывается один раз за итерацию. Возвращаемым типом у метода Parallel.For() является структура ParallelLoopResult, в которой содержатся сведения о выполненной части цикла.

    Пример использование параллельного цикла Parallel.For() представлен ниже:

    Parallel.For(0, 10, i =>
            {
                Console.WriteLine("{0}, Задача: {1}, Поток: {2}", i,
                   Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(10);
    
            });        Console.ReadLine();
    (рис 9.2) Результат работы программы с использованием метода Parallel.For()

    В одной из перегрузок этого метода Parallel.For() есть третий параметр Action<int, ParallelLoopState>. С помощью такой перегрузки метода, можно оказывать влияние на выполнение цикла с помощью методов Break() и Stop() объекта ParallelLoopState:

  • Stop() - применяется в алгоритмах поиска, где после нахождения результата, выполнять другие итерации не требуется;
  • Break() - используется для передачи циклу информации, что другие итерации после текущей итерации выполнять не требуется.
  • Пример кода с использованием объекта ParallelLoopState представлен ниже:

    Parallel.For(0, 100, (i,state) =>
            {
                Console.WriteLine("{0}, Задача: {1}, Поток: {2}", i,
                   Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(10);
                if (i == 55) state.Break();
    
            });
    (рис 9.3) Результат работы программы с использованием метода Parallel.For() и объекта ParallelLoopState

    Parallel.ForEach

    Метод Parallel.ForEach(), позволяет создавать распараллеленный вариант цикла foreach. Существует несколько форм метода Parallel.ForEach(). Одна из простых форм объявление данного метода приведена ниже:

    ForEach<TSource>(IEnumerable<TSource> source, Action<TSource> body);

    где source - обозначает коллекцию данных, обрабатываемых в цикле, body - метод, который будет выполняться на каждом шаге цикла.

    Пример реализации метода Parallel.ForEach(), представлен ниже:

    int[] data = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
    
                
                    Parallel.ForEach<int>(data, l =>
                    {
                        Console.WriteLine(l);
                    });
           
                Console.ReadLine();
    (рис 9.4) Результат работы программы с использованием метода Parallel.Foreach()

    Параллельное выполнение цикла методом Parallel.ForEach() также как и метод Parallel.For(), можно остановить, вызвав метод Break() для экземпляра объекта ParallelLoopState, передаваемого через параметр body, форма метода Parallel.Foreach() приведена ниже:

    ForEach<TSource>(IEnumerable<TSource> source,Action<TSource, ParallelLoopState> body);

    Пример реализации метода Parallel.ForEach(), с использованием объекта ParallelLoopState представлен ниже:

    char[] data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
    
      ParallelLoopResult result =
                    Parallel.ForEach(data, (l, state) =>
                    {
                        
                        Console.WriteLine(l);
                      if (l == 'e') state.Break();
                       
                    });
    (рис 9.5) Результат работы программы с использованием метода Parallel.Foreach() и объекта ParallelLoopState

    Для того что бы получить индекс параллельной версии цикла foreach используется следующая форма метода Parallel.Foreach():

    public static ParallelLoopResult ForEach<TSource> (IEnumerable<TSource> source, Action<TSource,ParallelLoopState,long> body

    где long - индекс цикла.

    Пример реализации метода Parallel.ForEach(), с использованием индексов цикла представлен ниже:

    char[] data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' };
    
                ParallelLoopResult result =
                    Parallel.ForEach(data, (l, state,i) =>
                    {
                        Console.WriteLine(l+" "+i);
                    });
    (рис 9.6) Результат работы программы с использованием метода Parallel.Foreach() и индексов цикла
    Вернуться к учебному плану