В этой серии постов будут выдержки из книги Стивена Клири. Просто разнообразные советы по работе с асинхронным кодом.
Возвращение завершённых задач
Задача: реализовать синхронный метод с асинхронной сигнатурой. Например, при модульном тестировании асинхронного кода, когда нужна простая заглушка или имитированная реализация для асинхронного интерфейса.
Решение:
1. Для возврата значения - использовать Task.FromResult:
public Task<int> GetValueAsync()2. Для методов, не имеющих возвращаемого значения - Task.CompletedTask:
=> Task.FromResult(42);
public Task DoSomethingAsync()3. Для возврата исключения - Task.FromException:
=> Task.CompletedTask;
public Task<T> ThrowsAsync<T>()4. Для отменённых задач - Task.FromCanceled:
=> Task.FromException<T>(new Exception());
public Task<int> GetValueAsync(CancellationToken ct) {
if (ct.IsCancellationRequested)
return Task.FromCanceled<int>(ct);
return Task.FromResult(13);
}
Если в синхронной реализации может произойти отказ, перехватывайте исключения и используйте Task.FromException:public Task DoSomethingAsync() {
try {
DoSomethingSynchronously();
return Task.CompletedTask;
}
catch (Exception ex)
{
return Task.FromException(ex);
}
}
Если вы реализуете асинхронный интерфейс синхронным кодом, избегайте любых форм блокировки. Избегайте блокирования с последующим возвращением завершённой задачи в асинхронном методе, если метод может быть реализован асинхронно. Если асинхронный метод блокируется, он не позволяет вызывающему потоку запускать другие задачи, что противоречит идее конкурентности и может привести к взаимоблокировке.Если вы регулярно используете
Task.FromResult, задачу можно кэшировать, чтобы не создавать объекты Task каждый раз:private static readonly Task<int> zeroTaskНа логическом уровне
= Task.FromResult(0);
…
Task<int> GetValueAsync()
=> zeroTask;
Task.FromResult, Task.FromException и Task.FromCanceled являются вспомогательными методами и сокращёнными формами обобщённого типа TaskCompletionSource<T>. TaskCompletionSource<T> представляет собой низкоуровневый тип, полезный для взаимодействия с другими формами асинхронного кода. В общем случае следует применять сокращенную форму Task.FromResult и родственные формы, если хотите вернуть уже завершённую задачу. TaskCompletionSource<T> используется для возвращения задачи, которая завершается в некоторый момент в будущем.Помимо этого, иногда требуется протестировать асинхронное поведение метода. В этом поможет
Task.Yield. Заметьте, что в отличие от вышеупомянутых методов, этот метод помечен как асинхронный и содержит await:public async Task<int> DoSomethingAsync()Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 2.
{
// Принудительно включить асинхронное поведение
await Task.Yield();
return 13;
}