【发布时间】:2019-10-17 19:11:10
【问题描述】:
我在使用 async 和 await 时遇到了一个奇怪的行为。如果我尝试等待手动创建的任务 T1,它本身应该等待另一个任务 T2,即使任务 T2 仍在等待,任务 T1 已经运行完成。
为了说明问题,我编写了一些代码。当我从这个例子运行'task1'时,输出是:
task1 运行完成 更新中...
当我改为运行“task2”时,输出总是:
更新中... task2 运行完成
有人解释为什么第一个等待表达式不依赖于内部等待吗?
private void OnLoaded(object sender, RoutedEventArgs e)
{
var task1 = new Task(async () => await UpdateAfterDelay());
task1.Start();
await task1;
Console.WriteLine("task1 ran to completion");
var task2 = Task.Run(async () => await UpdateAfterDelay());
await task2;
Console.WriteLine("task2 ran to completion");
}
private async Task UpdateAfterDelay()
{
await Task.Delay(2000);
Console.WriteLine("updating...");
}
【问题讨论】:
-
您遇到了奇怪的行为,因为您正在做非常奇怪的事情。您已经有一个返回任务的异步方法;你为什么要经历所有这些用异步 lambda 创建新任务的繁琐程序?如果您想要两个等待的两个任务,只需执行
await Update(); await Update();即可。你能解释一下为什么你试图构建这个可以简单表达的过于复杂和令人困惑的工作流程吗? -
Task<TResult>构造函数不理解异步委托。这意味着它不会将Func<Task>识别为特殊的东西。它认为Task只是一个正常的返回值。所以你最终会得到一个返回值Task<Task>。在这些情况下,您必须分别等待外部和内部Task的完成,这是通过await await实现的。因此,要使您的代码按预期运行,您必须进行两项小的更改:1)使用通用Task构造函数(new Task<Task>)而不是非通用构造函数。 2)等待两次结果任务。 -
@Theodor Zoulias 那行得通。谢谢。但是由于使用了 await 运算符,推断的类型不是 Task
。所以我仍然对为什么这不起作用感到困惑。如果我在不使用 await 的情况下声明表达式,编译器会通知我,调用不会等待,以便继续执行。这没有任何意义,因为使用 await 会导致相同的行为。 -
我试图将一大堆异步表达式封装在一个方法中。由于使用 async / await 需要您使整个 API 异步,因此我无法避免使这些方法异步(总而言之,我试图在不同的线程上执行计算密集型操作,并且之后必须更新我的 UI) .但不管我的代码如何,我现在对为什么这两种方法的行为不同感兴趣。由于两者都在采取行动回调,我不希望这样。如果我写“new Task(() => UpdateAfterDelay())”,省略 await 运算符会导致编译器警告。
-
您当前正在使用非泛型
Task构造函数,它接受Action类型的参数。不是Func。因此,您的UpdateAfterDelay方法的返回值将被丢弃。这成为一个问题,因为此方法的返回值为Task,而您无法等待此Task,因为未返回对它的引用。所以你的代码变得并行,被忽略的Task和task1.Start()之后的代码同时运行。您可能认为await task1;等待上述任务,但事实并非如此。它等待创建被忽略的内部任务的外部任务。
标签: c# async-await task