762
Глава.27 .Асинхронные.вычислительные.операции
автоматический запуск задания
по завершении предыдущего
Для написания масштабируемого программного обеспечения следует избегать
блокировки потоков. Вызов метода
Wait
или запрос свойства
Result
при неза-
вершенном задании приведет, скорее всего, к появлению в пуле нового потока, что
увеличит расход ресурсов и отрицательно скажется на расширяемости. К счастью,
существует способ узнать о завершении задания. Оно может просто инициировать
выполнение следующего задания. Вот как следует переписать предыдущий код,
чтобы избежать блокировки потоков:
// Создание объекта Task с отложенным запуском
Task t = Task.Run(() => Sum(CancellationToken.None, 10000));
// Метод ContinueWith возвращает объект Task, но обычно
// он не используется
Task cwt = t.ContinueWith(task => Console.WriteLine(
"The sum is: " + task.Result));
Теперь, как только задание, выполняющее метод
Sum
, завершится, оно инициирует
выполнение следующего задания (также на основе потока из пула), которое выведет
результат. Исполняющий этот код поток не блокируется, ожидая завершения каж-
дого из указанных заданий; он может в это время исполнять какой-то другой код
или, если это поток из пула, вернуться в пул для решения других задач. Обратите
внимание, что выполняющее метод
Sum
задание может завершиться до вызова ме-
тода
ContinueWith
. Впрочем, это не проблема, так как метод
ContinueWith
заметит
завершение задания
Sum
и немедленно начнет выполнение задания, отвечающего
за вывод результата.
Также следует обратить внимание на то, что метод
ContinueWith
возвращает
ссылку на новый объект
Task
(в моем коде она помещена в переменную
cwt
). При
помощи этого объекта можно вызывать различные члены (например, метод
Wait
,
Result
или даже
ContinueWith
), но обычно он просто игнорируется, а ссылка на
него не сохраняется в переменной.
Следует также упомянуть, что во внутренней реализации объект
Task
содержит
коллекцию
ContinueWith
. Это дает возможность несколько раз вызвать метод
ContinueWith
при помощи единственного объекта
Task
. Когда это задание завер-
шится, все задания из коллекции
ContinueWith
окажутся в очереди в пуле потоков.
Кроме того, при вызове метода
ContinueWith
можно установить флаги перечис-
ления
TaskContinuationOptions
. Первые шесть флагов —
None
,
PreferFairness
,
LongRunning
,
AttachedToParent
,
DenyChildAttach
и
HideScheduler
— аналогичны
флагам показанного ранее перечисления
TaskCreationOptions
. Вот как выглядит
тип
TaskContinuationOptions
:
[Flags, Serializ
able]
public enumTaskContinuationOptions {
None = 0x0000, // По умолчанию
// Сообщает планировщику, что задание должно быть поставлено
763
Задания
// на выполнение по возможности скорее
PreferFairness = 0x0001,
// Сообщает планировщику, что ему следует более активно
// создавать потоки в пуле потоков.
LongRunning = 0x0002,
// Всегда учитывается: присоединяет задание к его родителю
AttachedToParent = 0x0004,
// Если задача пытается присоединиться к родительской задаче,
// она интерпретируется как обычная, а не как дочерняя задача.
DenyChildAttach = 0x0008,
// Заставляет дочерние задачи использовать планировщик по умолчанию
// вместо родительского планировщика.
HideScheduler = 0x0010,
// Запрещает отмену до завершения предшественника.
LazyCancellation = 0x0020,
// Этот флаг устанавливают, когда требуется, чтобы поток,
// выполняющий первое задание, выполнил и задание ContinueWith.
// Если первое задание уже завершено, поток, вызывающий ContinueWith,
// выполняет задание ContinueWith
ExecuteSynchronously = 0x80000,
// Эти флаги указывают, когда запускать задание ContinueWith
NotOnRanToCompletion = 0x10000,
NotOnFaulted = 0x20000,
NotOnCanceled = 0x40000,
// Эти флаги являются комбинацией трех предыдущих
OnlyOnCanceled = NotOnRanToCompletion | NotOnFaulted,
OnlyOnFaulted = NotOnRanToCompletion | NotOnCanceld,
OnlyOnRanToCompletion = NotOnFaulted | NotOnCanceled,
}
При вызове метода
ContinueWith
флаг
TaskContinuationOptions.OnlyOn-
Canceled
показывает, что новое задание должно выполняться только в случае от-
мены предыдущего. Аналогично, флаг
TaskContinuationOptions.OnlyOnFaulted
дает понять, что выполнение нового задания должно начаться только после того,
как первое задание станет источником необработанного исключения. Ну а при
помощи флага
TaskContinuationOptions.OnlyOnRanToCompletion
вы програм-
мируете запуск нового задания только при условии, что предыдущее задание не
было отменено и не создало необработанного исключения, а было выполнено
полностью. Без этих флагов новое задание запускается вне зависимости от того,
как завершилось предыдущее. После завершения объекта
Task
автоматически
отменяются все его вызовы с незапущенными заданиями. Вот пример, демон-
стрирующий сказанное:
764
Do'stlaringiz bilan baham: |