814
Глава.28 .Асинхронные.операции.ввода-вывода
строить асинхронные серверы с хорошей масштабируемостью. В этой книге я не
смогу объяснить, как эта задача решается для каждого вида серверов, но могу хотя
бы указать, что следует искать в документации MSDN.
Построение асинхронных приложений Web Forms ASP.NET
: добавьте в файле
aspx
строку «Async=true» в директиве
page
и ознакомьтесь с описанием метода
RegisterAsyncTask
класса
System.Web.UI.Page
.
Построение асинхронных MVC-контроллеров ASP.NET
: объявите класс
контроллера производным от
System.Web.Mvc.AsyncController
и верните
Task
из метода действия.
Построение асинхронного обработчика ASP.NET
: объявите класс производным
от
System.Web.HttpTaskAsyncHandler
и переопределите его абстрактный метод
ProcessRequestAsync
.
Построение асинхронной службы WCF
: реализуйте службу как асинхронную
функцию, возвращающую
Task
или
Task
.
Отмена операций ввода-вывода
В общем случае Windows не предоставляет возможности отмены затянувшейся опе-
рации ввода-вывода. Многие разработчики хотели бы видеть такую возможность, но
реализовать ее достаточно сложно. Ведь если вы обращаетесь с запросом к серверу,
а потом решаете, что ответ вам больше не нужен, просто приказать серверу проиг-
норировать исходный запрос уже не удастся; нужно принять байты на клиентской
машине и отбросить их. Кроме того, возникает ситуация гонки — запрос на отмену
может поступить в то время, когда сервер передает ответ. И как должно поступить
ваше приложение? Вам придется обработать эту потенциальную ситуацию в своем
коде и решить, то ли проигнорировать данные, то ли обработать их.
Для упрощения этой задачи я рекомендую реализовать метод расширения
WithCancellation
, который расширяет
Task
(вам также понадобится
аналогичная перегрузка, расширяющая
Task
) следующим образом:
private struct Void { } // Из-за отсутствия необобщенного класса
// TaskCompletionSource.
private static async Task
WithCancellation(this Task originalTask,
CancellationToken ct) {
// Создание объекта Task, завершаемого при отмене CancellationToken
var cancelTask = new TaskCompletionSource();
// При отмене CancellationToken завершить Task
using (ct.Register(
815
Отмена.операций.ввода-вывода
t => ((Tas
kCompletionSource
)t).TrySetResult(new Void()),
cancelTask)) {
// Создание объекта Task, завершаемого при отмене исходного
// объекта Task или объекта Task от CancellationToken
Task any = await Task.WhenAny(originalTask, cancelTask.Task);
// Если какой-либо объект Task завершается из-за CancellationToken,
// инициировать OperationCanceledException
if (any == cancelTask.Task) ct.ThrowIfCancellationRequested();
}
// Выполнить await для исходного задания (синхронно); awaiting it
// если произойдет ошибка, выдать первое внутреннее исключение
// вместо AggregateException
return await originalTask;
}
Теперь этот метод расширения вызывается следующим образом:
public static async Task Go() {
// Создание объекта CancellationTokenSource, отменяющего себя
// через заданный промежуток времени в миллисекундах
var cts = new CancellationTokenSource(5000); // Чтобы отменить ранее,
var ct = cts.Token; // вызовите cts.Cancel()
try {
// Я использую Task.Delay для тестирования; замените другим методом,
// возвращающим Task
await Task.Delay(10000).WithCancellation(ct);
Console.WriteLine("Task completed");
}
catch (OperationCanceledException) {
Console.WriteLine("Task cancelled");
}
}
Do'stlaringiz bilan baham: