Почему мой TCS не ждет?
async
Ключевое слово действительно вызывает изменение CIL (даже если в методе нет ожидания), но в первую очередь это позволяет await
присутствовать.
Но я не ожидал, что произойдет следующее:
static void Main(string[] args)
{
Task t = Go();
t.Wait();
}
static async Task Go()
{
Console.WriteLine(1);
await AAA(3000);
Console.WriteLine(2);
}
static Task<object> AAA(int a) // <--- No `async`
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
Task.Delay(a).ContinueWith(b => tcs.SetResult(null));
return tcs.Task;
}
Этот принт:
1
(wait)
2
Но если я изменю
static Task<object> AAA(int a)
в
static async Task<object> AAA(int a)
Это печатает:
1
2
(no wait)
Вопрос
Почему я не вижу задержки? TCS разрешается только через три секунды. Между тем, задача не решена и ее следует ждать.
3 ответа
Без async
Ключевое слово, которое вы возвращаете TaskCompletionSource
задача от AAA
и поэтому вы ждете его завершения (что произойдет после завершения задержки).
Тем не менее, когда вы добавляете async
ключевое слово, задача, возвращаемая методом, является задачей конечного автомата, которая выполняется синхронно. Эта задача имеет внутри (в результате) TaskCompletionSource
это задача, но это не та задача, которую вы ждете.
Если вы хотите, чтобы этот метод ожидал TaskCompletionSource
задача, которую вы можете ждать внутреннюю задачу Task<Task>
:
await ((Task) await AAA(3000));
Или жду TaskCompletionSource
вместо того, чтобы вернуть его:
async Task AAA(int a)
{
TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();
Task.Delay(a).ContinueWith(b => tcs.SetResult(null));
await tcs.Task;
}
Или даже лучше, просто в ожидании Task.Delay
сам:
async Task<object> AAA(int a)
{
await Task.Delay(a);
return null;
}
Потому что, когда вы вернетесь Task
из асинхронного метода с типом возврата Task<object>
что вы получите Task<Task>
с вашей задачей (которую вы ожидаете ожидать) внутри. Вот что вы должны сделать:
static async Task<object> AAA(int a)
{
await Task.Delay(a);
return null;
}
Короче говоря, старайтесь избегать смешивания асинхронных и прямых задач в одном методе.
Как упоминал Сергей, посмотрите на этот пример в Visual Studio:
static async Task<int> AAA(int a) // <--- No `async`
{
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
Task.Delay(a).ContinueWith(b =>
{
tcs.SetResult(1);
});
return tcs.Task;
}
Вы получите ошибку, как это:
Поскольку это асинхронный метод, возвращаемое выражение должно иметь тип int, а не Task, поскольку ваш метод будет выполняться синхронно. Вот почему необходимо вернуть int
не Task
,