#c# #async-await
#c# #асинхронное ожидание
Вопрос:
class MyAwaitable
{
public MyAwaiter GetAwaiter()
{
return new MyAwaiter();
}
public class MyAwaiter : INotifyCompletion
{
public bool IsCompleted { get; }
public void GetResult()
{
Console.WriteLine("GetResult().");
}
public void OnCompleted(Action continuation)
{
Console.WriteLine("Continuation begins.");
continuation();
Console.WriteLine("Continuation ends.");
}
}
}
class Program
{
static async Task Main()
{
await new MyAwaitable();
Console.WriteLine("The last code.");
}
}
Вывод:
Начинается продолжение.
GetResult().
Последний код.
Продолжение заканчивается.
Вопрос
Я действительно не понимаю, почему Continuation begins.
это было раньше GetResult().
. По моей интуиции, GetResult()
должно быть первым.
Почему продолжение начинается перед GetResult()
?
Комментарии:
1. Просто в качестве примечания, вызов
continuation
внутриOnCompleted
может потенциально привести к переполнению стека, даже если пользовательский код выглядит довольно итеративным (см. Эту скрипку ).
Ответ №1:
continuation
это делегат, который выполняется, когда ожидаемое завершается. Обычно TaskAwaiter
ожидаемое сохранит continuation
в поле и запустит его, когда Task
завершится.
Сгенерированный компилятором конечный автомат использует это для перехода к следующему состоянию, которое, в свою очередь, вызовет .GetResult()
ожидаемую вещь (как для получения результата, так и для создания любых исключений);
Итак, continuation
это делегат, который в конечном итоге вызывает GetResult()
.
Вы можете найти, где OnCompleted
вызывается здесь. continuation
является результатом того, AsyncMethodBuilderCore.GetCompletionAction
из MoveNextRunner.Run
которого оно создается, который вызывает MoveNext
конечный автомат.
Если вы посмотрите на сгенерированный компилятором код для вашего асинхронного метода, вы увидите, что он MoveNext
вызывает awaiter.GetResult()
.
Чтобы увидеть, как Task
работает здесь, начните с TaskAwaiter.OnCompleted
и посмотрите, что он вызывает Task.SetContinuationForAwait
, который вызывает AddTaskContinuation
, который в конечном итоге сохраняет его в m_continuationObject
. Затем это вызывается в Task.FinishContinuations
.