не может ждать асинхронной лямбды

Учти это,

Task task = new Task (async () =>{
    await TaskEx.Delay(1000);
});
task.Start();
task.Wait(); 

Вызов task.Wait () не ожидает завершения задачи, и следующая строка выполняется немедленно, но если я заключу асинхронное лямбда-выражение в вызов метода, код будет работать должным образом.

private static async Task AwaitableMethod()
{
    await TaskEx.Delay(1000);    
}

затем (обновлено в соответствии с комментарием svick)

await AwaitableMethod(); 

person kennyzx    schedule 24.10.2012    source источник
comment
В AwaitableMethod вы фактически возвращаетесь и вызываете Wait для задачи, возвращенной методом .Delay () (я предполагаю, что она возвращает Task). В асинхронной лямбде вы вызываете Wait на Task task. Но все же у меня нет объяснений.   -  person Mario S    schedule 24.10.2012
comment
Вы должны быть очень осторожны при смешивании await с Wait(). Во многих случаях это может привести к тупикам.   -  person svick    schedule 24.10.2012
comment
@svick нашел отличный пример о смешивании await с Wait()   -  person kennyzx    schedule 25.10.2012


Ответы (2)


В вашем примере лямбда-выражения, когда вы вызываете task.Wait(), вы ждете созданную вами новую задачу, а не возвращаемую ею задачу задержки. Чтобы получить желаемую задержку, вам нужно также дождаться выполнения результирующей задачи:

Task<Task> task = new Task<Task>(async () => {
    await Task.Delay(1000);
});
task.Start();
task.Wait(); 
task.Result.Wait();

Вы можете избежать создания новой задачи и иметь дело только с одной задачей вместо двух:

Func<Task> task = async () => {
    await TaskEx.Delay(1000);
};
task().Wait();
person Joe Daley    schedule 24.10.2012
comment
Если первое ожидание происходит после большой обработки, вам все равно могут понадобиться двойные задачи. Вместо task.Result.Wait() вы также можете использовать task.Unwrap().Wait() (или Unwrap<T>() для непустых методов). Новые Task.Run методы автоматически разворачиваются, поэтому вы ждете только ожидаемую задачу. - person Nelson Rothermel; 09.09.2014
comment
Как новичок, у меня сложилось впечатление, что они могли бы лучше справиться с ключевым словом async; это очень сбивает с толку. - person drowa; 12.12.2014
comment
Можно ли запустить задачу контейнера без запуска вложенной задачи? - person drowa; 12.12.2014
comment
@drowa async ожидает, что функции будут возвращать Горячие задачи, которые будут возвращены из уже запущенных функций. Если вы ожидаете задачу, которая не была запущена и не будет запущена, программа никогда не вернется из ожидания. Единственный способ не запускать вложенную задачу - это, находясь в функции контейнера, никогда не вызывать внутреннюю функцию, которая создает вложенную задачу. - person Scott Chamberlain; 19.03.2015
comment
Ссылка на потенциальные ловушки не работает и теперь имеет вид devblogs.microsoft.com/pfxteam/ - person VoteCoffee; 16.01.2020

Вам нужно использовать TaskEx.RunEx.

Он изначально поддерживает запуск async методов на TaskPool путем внутреннего ожидания внутренней задачи. В противном случае вы столкнетесь с проблемой, с которой столкнетесь, где ожидается только внешняя задача, которая, очевидно, завершается немедленно, оставляя либо задачу, которая все еще требует ожидания, либо, в вашем случае (и даже хуже), недействительную лямбду, которая не может быть ждал.

В качестве альтернативы вы можете дождаться выполнения задачи дважды, при условии, что вы правильно построите внешнюю задачу (которой в настоящее время нет).

Текущий код (фиксированный):

Task task = new Task<Task>(async () =>{
    await TaskEx.Delay(1000);
});

task.Start();
var innerTask = await task;
await innerTask;

Используя TaskEx.RunEx:

Task task = TaskEx.RunEx(async () =>{ // Framework awaits your lambda internally.
    await TaskEx.Delay(1000);
});

await task;
person Lawrence Wagerfield    schedule 24.10.2012
comment
Хорошее объяснение, но код TaskEx.Run не работает, все та же проблема. - person kennyzx; 24.10.2012
comment
Ах, прости! Я использую .NET 4.5 ... Я хотел написать TaskEx.RunEx. Сравните его подпись с TaskEx.Run - вы поймете, почему он специально предназначен для запуска асинхронных методов. - person Lawrence Wagerfield; 24.10.2012