Опубликован: 23.01.2013 | Уровень: для всех | Доступ: платный | ВУЗ: Томский политехнический университет
Лекция 6:

Параллелизм задач

Продолжения

Для того что бы упорядочить выполнение задач в программе, т.е. к примеру что бы Задача 3 выполнялась сразу же после выполнения Задачи 2, а Задача 2 после завершения Задачи 1, необходимо использовать метод ContinueWith() класса - Task. Форма объявление данного метода представлена ниже:

public Task ContinueWith(Action<Task>)

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

Task task1 = Task.Factory.StartNew (() => Console.Write ("Hello "));
Task task2 = task1.ContinueWith (main => Console.Write ("World!"));

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

 Результат выполнения программы с использование метода ContinueWith()

увеличить изображение
Рис. 7.7. Результат выполнения программы с использование метода ContinueWith()

Продолжения и Task <TResult>

В классе Task<TResult>, также можно использовать продолжение и возвращать некоторые данные, с использованием последовательности вычисления:

Task.Factory.StartNew<int>(() => 10)
     .ContinueWith(main => main.Result * 2)
     .ContinueWith(main => Math.Sqrt(main.Result))
     .ContinueWith(main => Console.WriteLine(main.Result));
 Результат выполнения программы с использование продолжения в классе Task<TResult>

увеличить изображение
Рис. 7.8. Результат выполнения программы с использование продолжения в классе Task<TResult>

Продолжения и исключения

Продолжение может определить, было ли сгенерировано исключение родительской задачей, с помощью свойства Exception родительской задачи. Следующий фрагмент кода будет выводить подробную информацию об исключении NullReferenceException:

Task task1 = Task.Factory.StartNew (() => { throw null; });
Task task2 = task1.ContinueWith (main => Console.Write (main.Exception));
 Результат выполнения программы с использование продолжения и обработки исключения NullReferenceException

увеличить изображение
Рис. 7.9. Результат выполнения программы с использование продолжения и обработки исключения NullReferenceException

Продолжения и дочерние задачи

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

TaskCreationOptions main = TaskCreationOptions.AttachedToParent;
Task.Factory.StartNew (() =>
{
  Task.Factory.StartNew (() => { throw null; }, main);
  Task.Factory.StartNew (() => { throw null; }, main);
  Task.Factory.StartNew (() => { throw null; }, main);
})
.ContinueWith (p => Console.WriteLine (p.Exception),
                    TaskContinuationOptions.OnlyOnFaulted);
 Результат выполнения программы с использование продолжения и дочерних задач

увеличить изображение
Рис. 7.10. Результат выполнения программы с использование продолжения и дочерних задач

Продолжения предыдущих задач

Метод ContinueWhenAll() используется для того, что бы выполнить продолжение конкретной задачи, после выполнения нескольких предыдущих задач:

Task<int> task1 = Task.Factory.StartNew (() => 200);
Task<int> task2 = Task.Factory.StartNew (() => 200);
 
Task<int> task3 = Task<int>.Factory.ContinueWhenAll (
  new[] { task1, task2 }, tasks => tasks.Sum (t => t.Result));
 Console.WriteLine (task3.Result);
 Результат выполнения программы с использование метода Метод ContinueWhenAll()

увеличить изображение
Рис. 7.11. Результат выполнения программы с использование метода Метод ContinueWhenAll()

Несколько продолжений одной задачи

Метод ContinueWith() позволяет создавать несколько продолжений одной задачи. Когда предыдущая задача завершается, все продолжения запускаются одновременно. Пример использования метода ContinueWith() представлен ниже:

var t = Task.Factory.StartNew (() => Thread.Sleep (1000));
t.ContinueWith (main => Console.Write ("X"));
t.ContinueWith (main => Console.Write ("Y"));
 Результат выполнения программы с использование метода ContinueWith()

увеличить изображение
Рис. 7.12. Результат выполнения программы с использование метода ContinueWith()

Планировщики заданий и пользовательский интерфейс

Планировщик задач (task scheduler) назначает задания определенным потокам. Все задания связаны с определенным планировщиком, который представлен абстрактным классом TaskScheduler. .Net Framework предоставляет две конкретные реализации:

  • Планировщик по умолчанию (default scheduler), который работает совместно с пулом потоков CLR;
  • Планировщик контекста синхронизации (synchronization context scheduler), который предназначен для упрощения работы с WPF и Windows Forms, которые требуют, чтобы обращение к элементам пользовательского интерфейса и элементам управления происходило только из потока, в котором они были созданы.

Что бы в фоновом режиме получить данные от Web-сервиса и затем, на основе полученных результатов, обновить метку (label) с именем lblResult. Разбиваем эту задачу на две подзадачи:

  1. Вызвать метод для получения данных от Web-сервиса (родительская задача).
  2. Обновить lblResult на основе полученных результатов (задача-продолжение).

Если указать для задачи-продолжения планировщик контекста синхронизации, полученный при создании окна, можно обновить lblResult:

public partial class MyWindow : Window
{
  TaskScheduler _uiScheduler;   // Объявляем TaskScheduler
  public MyWindow()
  {    
    InitializeComponent();
 
    _uiScheduler = TaskScheduler.
FromCurrentSynchronizationContext();
 
    Task.Factory.StartNew<string> (SomeComplexWebService)
      .ContinueWith (main => lblResult.Content = main.Result, _uiScheduler);
  }
 
  string SomeComplexWebService() { ... }
}
Владимир Каширин
Владимир Каширин

Вопрос по Курсу: "Параллельное программирование с использованием MS VisualStudia 2010".

При компиляции Самостоятельного задания (одновременная отрисовка прямоугольников, эллипсов и выдача в текст-бокс случайного числа) среда предупреждает: suspend - устаревшая команда; примените monitor, mutex и т.п.

Создаётся впечатление, что Задание создано в более поздней среде, чем VS 2010.

Александр Гаврилов
Александр Гаврилов
Россия
Роман Дмитриев
Роман Дмитриев
Россия, Москва