Using System.Threading.Tasks;

class DemoCancelTask {

// Метод, исполняемый как задача, static void MyTask(Object ct) {

CancellationToken cancelTok = (CancellationToken) ct;

// Проверить, отменена ли задача, прежде чем запускать ее. cancelTok.ThrowIfCancellationRequested();

Console.WriteLine("MyTask() запущен");

for(int count = 0; count < 10; count++) {

// В данном примере для отслеживания отмены задачи применяется опрос, if(cancelTok.IsCancellationRequested) {

Console.WriteLine("Получен запрос на отмену задачи."); cancelTok.ThrowIfCancellationRequested();

}

Thread.Sleep(500);

Console.WriteLine("В методе MyTask() подсчет равен " + count );

}

Console.WriteLine("MyTask завершен");

}

static void Main() {

Console.WriteLine("Основной поток запущен.");

// Создать объект источника признаков отмены.

CancellationTokenSource cancelTokSrc = new CancellationTokenSource();

// Запустить задачу, передав признак отмены ей самой и делегату.

Task tsk = Task.Factory.StartNew(MyTask, cancelTokSrc.Token,

CancelTokSrc.Token);

// Дать задаче возможность исполняться вплоть до ее отмены.

Thread.Sleep(2000); try {

// Отменить задачу. cancelTokSrc.Cancel();

// Приостановить выполнение метода Main() до тех пор,

// пока не завершится задача tsk. tsk.Wait();

} catch (AggregateException exc) { if(tsk.IsCanceled)

Console.WriteLine("ХпЗадача tsk отменена\п");

// Для просмотра исключения снять комментарии со следующей строки кода:

// Console.WriteLine(exc);

} finally {

Tsk.Dispose(); cancelTokSrc.Dispose();

}

Console.WriteLine("Основной поток завершен.");

}

}

Ниже приведен результат выполнения этой программы. Обратите внимание на то что задача отменяется через 2 секунды.

Основной поток запущен.

MyTask() запущен

Получен запрос на отмену задачи.

Задача tsk отменена

Основной поток завершен.

Как следует из приведенного выше результата, выполнение метода MyTask () отменяется в методе Main () лишь две секунды спустя. Следовательно, в методе MyTask () выполняются четыре шага цикла. Когда же перехватывается исключение AggregateException, проверяется состояние задачи. Если задача tsk отменена, что и должно произойти в данном примере, то об этом выводится соответствующее сообщение. Следует, однако, иметь в виду, что когда сообщение AggregateException генерируется в ответ на отмену задачи, то это еще не свидетельствует об ошибке, а просто означает, что задача была отменена.

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

 

Другие средства организации задач

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

В предыдущем разделе было дано краткое описание исключения AggregateException, но у него имеются также другие особенности, которые могут оказаться весьма полезными. К их числу относится метод Flatten (), применяемый для преобразования любых внутренних исключений типа AggregateException в единственное исключение AggregateException. Другой метод, Handle (), служит для обработки исключения, составляющего совокупное исключение AggregateException.

При создании задачи имеется возможность указать различные дополнительные параметры, оказывающие влияние на особенности ее исполнения. Для этой цели указывается экземпляр объекта типа TaskCreationOptions в конструкторе класса Task или же в фабричном методе StartNew (). Кроме того, в классе TaskFactory доступно целое семейство методов FromAsync (), поддерживающих модель асинхронного программирования (АРМ — Asynchronous Programming Model).

Как упоминалось ранее в этой главе, задачи планируются на исполнение экземпляром объекта класса TaskScheduler. Как правило, для этой цели предоставляется планировщик, используемый по умолчанию в среде .NET Framework. Но этот планировщик может быть настроен под конкретные потребности разработчика. Кроме того, допускается применение специализированных планировщиков задач.

Класс Parallel

В примерах, приведенных до сих пор в этой главе, демонстрировались ситуации, в которых библиотека TPL использовалась таким же образом, как и класс Thread. Но это было лишь самое элементарное ее применение, поскольку в TPL имеются и другие средства. К их числу относится класс Parallel, который упрощает параллельное исполнение кода и предоставляет методы, рационализирующие оба вида параллелизма: данных и задач.

Класс Parallel является статическим, и в нем определены методы For (), For Each () и Invoke (). У каждого из этих методов имеются различные формы. В частности, метод For ( ) выполняет распараллеливаемый цикл for, а метод ForEach () — распараллеливаемый цикл foreach, и оба метода поддерживают параллелизм данных. А метод Invoke () поддерживает параллельное выполнение двух методов или больше. Как станет ясно дальше, эти методы дают преимущество реализации на практике распространенных методик параллельного программирования, не прибегая к управлению задачами или потоками явным образом. В последующих разделах каждый из этих методов будет рассмотрен более подробно.

Распараллеливание задач методом Invoke ()

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

public static void Invoke(params Action[] actions)