Глава.27 .Асинхронные.вычислительные.операции
завершенные задания, заставляя поток продолжить исполнение. Если проис-
ходит тайм-аут, метод возвращает –1. Отмена же метода посредством структуры
CancellationToken
приводит к исключению
OperationCanceledException
.
ВниМание
Если.вы.ни.разу.не.вызывали.методы.Wait.или.Result.и.не.обращались.к.свойству.
Exception.класса.Task,.код.не.«узнает».о.появившихся.исключениях .Иначе.говоря,.
вы.не.получите.информации.о.том,.что.программа.столкнулась.с.неожиданной.
проблемой .Для.распознавания.скрытых.исключений.можно.зарегистрировать.ме-
тод.обратного.вызова.со.статическим.событием.UnobservedTaskException.класса.
TaskScheduler .При.уничтожении.задания.со.скрытым.исключением.в.ходе.уборки.
мусора.это.событие.активизируется.потоком.финализации.уборщика.мусора.CLR .
После.этого.обработчику.события.передается.объект.UnobservedTaskExceptionEve
ntArgs,.содержащий.скрытое.исключение.AggregateException
Статический метод
WaitAll
класса
Task
блокирует вызывающий поток до за-
вершения всех объектов
Task
в массиве. Метод возвращает значение
true
после
завершения всех объектов и значение
false
, если истекает время ожидания. От-
мена этого метода посредством структуры
CancellationToken
также приводит
к исключению
OperationCanceledException
.
Отмена задания
Для отмены задания можно воспользоваться объектом
CancellationTokenSource
.
Впрочем, сначала нам следует отредактировать метод
Sum
, дав ему возможность
работать со структурой
CancellationToken
:
private static Int32 Sum(CancellationTokenct, Int32 n) {
Int32 sum = 0;
for (; n > 0; n--) {
// Следующая строка приводит к исключению OperationCanceledException
// при вызове метода Cancel для объекта CancellationTokenSource,
// на который ссылается маркер
ct.ThrowIfCancellationRequested();
checked { sum += n; } // при больших n появляется
// исключение System.OverflowException
}
return sum;
}
В этом коде цикл вычислительной операции периодически вызывает метод
ThrowIfCancellationRequested
класса
CancellationToken
, чтобы проверить,
не появился ли запрос на отмену операции. Этот метод аналогичен свойству
IsCancellationRequested
класса
CancellationToken
, рассмотренному ранее.
761
Задания
Однако при отмене объекта
CancellationTokenSource
метод генерирует исклю-
чение
OperationCanceledException
. Причиной исключения становится тот факт,
что в отличие от рабочих элементов, запущенных методом
QueueUserWorkItem
класса
ThreadPool
, задания поддерживают концепцию выполнения и даже могут
возвращать значение. Следовательно, нужен способ, позволяющий отличить за-
вершенное задание от незавершенного. Именно для этого применяется исключение.
Создадим объекты
CancellationTokenSource
и
Task
:
CancellationTokenSource cts = new CancellationTokenSource();
Task t = new Task(() => Sum(cts.Token, 10000), cts.Token);
t.Start();
// Позднее отменим CancellationTokenSource, чтобы отменить Task
cts.Cancel(); // Это асинхронный запрос, задача уже может быть завершена
try {
// В случае отмены задания метод Result генерирует
// исключение AggregateException
Console.WriteLine("The sum is: " + t.Result); // Значение Int32
}
catch (AggregateException x) {
// Считаем обработанными все объекты OperationCanceledException
// Все остальные исключения попадают в новый объект AggregateException,
// состоящий только из необработанных исключений
x.Handle(e => e is OperationCanceledException);
// Строка выполняется, если все исключения уже обработаны
Console.WriteLine("Sum was canceled");
}
Создаваемый объект
Task
можно связать с объектом
CancellationToken
,
передав его конструктору
Task
(как показано ранее). Если отменить объект
CancellationToken
до планирования задания, задание тоже будет отменено
1
. Однако
если задание уже начало выполняться (при помощи метода
Start
), его код должен
в явном виде поддерживать отмену. К сожалению, несмотря на то что с объектом
Task
связан объект
CancellationToken
, у вас нет доступа к последнему. То есть вы
должны каким-то образом поместить
тот же самый
объект
CancellationToken
,
который использовался при создании объекта
Task
, в код задания. Проще всего при
написании этого кода воспользоваться лямбда-выражением и «передать» объект
CancellationToken
в качестве переменной замыкания (что, собственно, и было
сделано в предыдущем примере).
1
Попытка отменить задание до начала его выполнения приводит к появлению исключения
InvalidOperationException.
Do'stlaringiz bilan baham: |