c# - 为什么 Task.Delay 在这种情况下不起作用

标签 c# async-await

我正在测试 async,我发现了这种我无法理解的情况:

var watch = Stopwatch.StartNew();

var t1 = Task.Factory.StartNew(async () =>
{
    await Task.Delay(2000);

    return 2;
});

var t2 = Task.Factory.StartNew(() =>
{
    Task.Delay(1000);

    return 1; 
});

await Task.WhenAll(t1, t2);

var result = watch.ElapsedMilliseconds;

我很想明白为什么结果总是0!为什么不是 1000、2000 或两个任务的总和 3000?为什么 Task.WhenAll 不等待任务完成?

最佳答案

好吧,那么,第二个比较简单,所以让我们来处理那个。

对于第二个任务,t2 , 你不对 Task.Delay(1000) 的结果做任何事情.你不await它,你不Wait它等。鉴于该方法不是 async我以为你的意思是它是一个阻塞等待。为此,您需要添加 Wait()Delay 的结尾调用使其成为阻塞等待,或者只使用 Thread.Sleep() .


对于第一个任务,您被使用 var 的事实所困扰。 .当你不使用 var 时发生的事情会更清楚:

Task<Task<int>> t1 = Task.Factory.StartNew(async () =>
{
    await Task.Delay(2000);

    return 2;
});

您正在返回 int 的任务中的一个任务,不只是一个 Taskint .一旦内部任务完成启动,外部任务就“完成”了。当您使用 WhenAll您不关心外部任务何时完成,您关心的是内部 任务何时完成。有多种方法可以处理此问题。一种是使用 Unwrap .

Task<int> t1 = Task.Factory.StartNew(async () =>
{
    await Task.Delay(2000);

    return 2;
}).Unwrap();

现在您有了预期的 Task<int>WhenAll正如预期的那样,至少需要 2000 毫秒。您还可以添加另一个 await调用做同样的事情:

Task<int> t1 = await Task.Factory.StartNew(async () =>
{
    await Task.Delay(2000);

    return 2;
});

正如斯维克所说 in a comment另一种选择是只使用 Task.Run而不是 StartNew . Task.Run对于采用 Func<Task<T>> 的方法有一组特殊的重载并返回 Task<T>并自动为您打开它们:

Task<int> t1 = Task.Run(async () =>
{
    await Task.Delay(2000);

    return 2;
});

因此,最好使用 Task.Run作为创建异步 lambda 时的默认选项,因为它会为你“处理”这个问题,尽管在你不能使用 Task.Run 的复杂情况下最好注意它。 .


最后,我们讨论了您没有做的选项,这可能是您在这种情况下实际应该做的。自 Task.Delay已经返回一个任务,没有必要把它放在 StartNew 中首先。而不是创建嵌套任务并使用 Unwrap你不能一开始就把它包起来:

var t3 = Task.Delay(3000);
await Task.WhenAll(t1, t2, t3);

如果您实际上只想等待一段固定的时间,那么您应该这样做。

关于c# - 为什么 Task.Delay 在这种情况下不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14071606/

相关文章:

C# 泛型和 void 的特例

c# - 如何将后台任务添加到 Windows 应用商店应用程序

c# - 使用 .NET 将文件发布到服务器 HttpWebRequest 或 WebClient

c# - 使用正则表达式搜索字符串

c# - 服务器应用程序的多个客户端超过 15 个客户端处理问题

c# - 需要有关属性(property)使用权的进修类(class)

c# - 为什么 HttpClient.PostAsyc().Result 可以用在同步方法中?

c# - 如何修复错误 : Cannot call the requested method (GetBasicPropertiesAsync). 之前对此方法的调用正在挂起

c# - 为什么使用 Task.Run() 仍然阻塞我的 UI?

c# - 从按钮取消异步任务