等待 Task.Delay() 与 Task.Delay().Wait()

Posted

技术标签:

【中文标题】等待 Task.Delay() 与 Task.Delay().Wait()【英文标题】:await Task.Delay() vs. Task.Delay().Wait() 【发布时间】:2015-01-04 02:28:37 【问题描述】:

在 C# 中我有以下两个简单的例子:

[Test]
public void TestWait()

    var t = Task.Factory.StartNew(() =>
    
        Console.WriteLine("Start");
        Task.Delay(5000).Wait();
        Console.WriteLine("Done");
    );
    t.Wait();
    Console.WriteLine("All done");


[Test]
public void TestAwait()

    var t = Task.Factory.StartNew(async () =>
    
        Console.WriteLine("Start");
        await Task.Delay(5000);
        Console.WriteLine("Done");
    );
    t.Wait();
    Console.WriteLine("All done");

第一个示例创建一个打印“开始”的任务,等待 5 秒打印“完成”,然后结束该任务。我等待任务完成,然后打印“全部完成”。当我运行测试时,它按预期运行。

第二个测试应该具有相同的行为,只是由于使用了 async 和 await,Task 内部的等待应该是非阻塞的。但是这个测试只打印“开始”,然后立即“全部完成”,永远不会打印“完成”。

我不知道为什么会出现这种行为:S 任何帮助将不胜感激 :)

【问题讨论】:

Task.Delay 是非阻塞的。我看不出你为什么要使用第二个构造。 @RoyDictus 他们都有自己的问题。你永远不应该打电话给Task.Wait() Waiting for async/await inside a task 的可能重复项 在 Main() 方法中不能使用“等待”。您必须使用 Wait() 或旧的 Thread.Sleep()。 【参考方案1】:

第二个测试有两个嵌套任务,您正在等待最外面的一个,要解决这个问题,您必须使用t.Result.Wait()t.Result 获取内部任务。

第二种方法大致相当于这样:

public void TestAwait()

  var t = Task.Factory.StartNew(() =>
            
                Console.WriteLine("Start");
                return Task.Factory.StartNew(() =>
                
                    Task.Delay(5000).Wait(); Console.WriteLine("Done");
                );
            );
            t.Wait();
            Console.WriteLine("All done");

通过调用t.Wait(),您正在等待立即返回的最外层任务。


处理这种情况的最终“正确”方法是完全放弃使用Wait,而只使用await。一旦将 UI 附加到异步代码,Wait 可能会导致 deadlock issues。

    [Test]
    public async Task TestCorrect() //note the return type of Task. This is required to get the async test 'waitable' by the framework
    
        await Task.Factory.StartNew(async () =>
        
            Console.WriteLine("Start");
            await Task.Delay(5000);
            Console.WriteLine("Done");
        ).Unwrap(); //Note the call to Unwrap. This automatically attempts to find the most Inner `Task` in the return type.
        Console.WriteLine("All done");
    

最好使用Task.Run 来启动您的异步操作:

    [TestMethod]
    public async Task TestCorrect()
    
        await Task.Run(async () => //Task.Run automatically unwraps nested Task types!
        
            Console.WriteLine("Start");
            await Task.Delay(5000);
            Console.WriteLine("Done");
        );
        Console.WriteLine("All done");
    

【讨论】:

感谢工作:) 顺便说一句,使用 Task.Run 而不是 Task.Factory.StartNew 总是可以的,或者在某些情况下我需要 Task.Factory.StartNew 而 Task.Run 不能处理? :) Task.Run(someAction);是一个方便的包装器,等效于 Task.Factory.StartNew(someAction, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

以上是关于等待 Task.Delay() 与 Task.Delay().Wait()的主要内容,如果未能解决你的问题,请参考以下文章

如何对包含Task.Delay的代码进行单元测试?

何时使用Task.Delay,何时使用Thread.Sleep?

15.3 Task Task.Yield和Task.Delay说明

REST API 中的 Thread.sleep 与 Task.delay

为啥 Task.Delay 不能在 Azure Function App 中工作?

为什么Task.Delay打破了线程的STA状态?