Ключевые этапы работы:
IAsyncStateMachine
:
// Исходный код
public async Task<int> GetDataAsync()
{
var data = await FetchAsync();
return Process(data);
}
// Преобразуется в:
struct StateMachine : IAsyncStateMachine
{
public int state;
public AsyncTaskMethodBuilder<int> builder;
private TaskAwaiter awaiter;
void MoveNext()
{
// Логика выполнения с разбиением по await
}
}
При вызове async-метода:
При встрече await:
При завершении операции:
Характеристика | async Task | async void |
---|---|---|
Возвращаемое значение | Task или Task<T> | Нет возвращаемого значения |
Обработка ошибок | Исключения в Task.Exception | Критические исключения в AppDomain |
Ожидание завершения | Можно await | Невозможно отследить завершение |
Использование | Рекомендуется всегда | Только для обработчиков событий |
async void DangerousMethod()
{
throw new InvalidOperationException("Ошибка");
}
// Вызов:
try
{
DangerousMethod(); // Исключение приведет к краху приложения
}
catch {} // Не перехватит исключение
Отсутствие возможности ожидания:
Нарушение архитектуры:
Допустимые случаи:
button.Click += async (sender, e) =>
{
await LoadDataAsync();
};
Паттерн для обертки:
public async Task MethodAsync()
{
try
{
await SomeOperationAsync();
}
catch (Exception ex)
{
Logger.LogError(ex);
throw;
}
}
// Вместо async void:
public void MethodAsyncWrapper() => _ = MethodAsync();
Для методов, которые часто завершаются синхронно:
public async ValueTask<int> GetCachedDataAsync()
{
if (cache.TryGetValue(key, out var data))
return data; // Синхронный возврат
return await FetchFromNetworkAsync(); // Асинхронная операция
}
var data = await networkService.GetDataAsync()
.ConfigureAwait(false); // Продолжение в потоке из пула
async/await преобразует метод в машину состояний, позволяя писать асинхронный код в синхронном стиле. Async void методы опасны из-за необрабатываемых исключений и нарушают композицию async-кода - используйте их только для обработчиков событий с должной обработкой ошибок.