【问题标题】:Task.Wait doesn't wait for async method completionTask.Wait 不等待异步方法完成
【发布时间】:2019-10-19 20:13:06
【问题描述】:

代码如下:

static async Task Main(string[] args)
{
    var t = new Task(async () => await AsyncTest());
    t.Start();
    t.Wait();
    Console.WriteLine("Main finished");
}

private static async Task AsyncTest()
{
    Thread.Sleep(2000);
    await Task.Delay(2000);
    Console.WriteLine("Method finished");
}

我的期望是 t.Wait() 实际上会等待 AsyncTest 方法完成,输出将是:

Method finished 
Main finished

实际上输出只有Main finished。当您在 AsyncTest 中点击 await Task.Delay(2000) 时,Wait() 就完成了。如果将 t.Wait() 替换为 await t / await Task.WhenAll(t) / Task.WaitAll(t),也会发生同样的情况。

解决方法是将方法重写为同步实现或直接在AsyncTest()上调用Wait()。然而,问题是它为什么会以这种奇怪的方式工作?

附:这是代码的简化版本。我试图实现延迟任务执行。实际上Task对象是由程序的一部分创建的,然后在某些特定条件下由另一部分执行。

UPD:将 var t = new Task(async () => await AsyncTest()) 重写为 var t = new Task(()=> AsyncTest().Wait()) 也可以解决问题。虽然我仍然不太明白为什么 Task 不能在委托中使用 async/await 正常工作。

【问题讨论】:

  • 不要使用var task = new Task(...); task.Start();,只使用Task.Run
  • 在我的真实程序中,任务已延迟执行。任务对象在一个地方创建,然后在程序的另一部分执行特定条件后。
  • @WiktorZychla AsyncTest 返回一个任务,但 async () => await AsyncTest() lambda 将其丢弃,因为它本身就是一个 Action (void)。 () => AsyncTest().Wait() 会“修复”它(由于Wait() 仍然会出错)。
  • var t1 = new Task<Task>(async () => await AsyncTest()); var t2 = t1.Unwrap(); t1.Start(); t2.Wait();

标签: c# .net asynchronous


【解决方案1】:

我仍然不太明白为什么 Task 不能在委托中使用 async/await 正常工作。

因为Task 构造函数仅用于创建Delegate Tasks - 即表示要运行的同步代码的任务。由于代码是同步的,您的async lambda 被视为async void lambda,这意味着Task 实例不会异步等待AsyncTest 完成。

更重要的是,the Task constructor should never, ever be used in any code, anywhere, for any reason。它的有效用例几乎为零。

Task.Run 可以很好地替代 Task.Task,它确实理解 async lambdas。

在我的真实程序中,Task 已延迟执行。任务对象在一个地方创建,然后在程序的另一部分执行特定条件后。

在这种情况下,请使用asynchronous delegate。具体来说,Func<Task>

static async Task Main(string[] args)
{
    Func<Task> func = AsyncTest;

    // Later, when we're ready to run.
    await func();
    Console.WriteLine("Main finished");
}

private static async Task AsyncTest()
{
    Thread.Sleep(2000);
    await Task.Delay(2000);
    Console.WriteLine("Method finished");
}

【讨论】:

    【解决方案2】:

    来自@JonSkeet 的quote

    您的异步方法只返回 void,这意味着没有简单的方法 等待它完成的任何事情。 (你应该几乎总是 避免使用异步 void 方法。它们实际上只适用于 为了订阅事件。)

    所以看看你的这行代码:

    var t = new Task(async () => await AsyncTest());
    

    Task构造函数的签名:

        public Task(Action action);
        public Task(Action action, CancellationToken cancellationToken);       
        public Task(Action action, TaskCreationOptions creationOptions);
        public Task(Action<object> action, object state);
        public Task(Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions);
        public Task(Action<object> action, object state, CancellationToken cancellationToken);
        public Task(Action<object> action, object state, TaskCreationOptions creationOptions);
        public Task(Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions);
    

    它们都是Actions,如您所知,Action 具有void 返回类型。

    static async Task Main(string[] args)
    {
        // best way to do it
        await AsyncTest();
    
    
        Console.WriteLine("Main finished");
    }
    
    private static async Task AsyncTest()
    {
        // Don't use thread sleep, await task delay is fine
        // Thread.Sleep(2000);
    
        await Task.Delay(2000);
        Console.WriteLine("Method finished");
    }
    

    【讨论】:

    • 虽然这个答案有道理,但您似乎复制了 this Jon's answer 的解释。
    • @WiktorZychla 是的,第一段来自传奇 JonSkeet,我最近编辑了答案,但离开了笔记本电脑并忘记发送更改。
    【解决方案3】:

    不要使用var task = new Task(...); task.Start();,只使用Task.Run

    在第一种情况下使用构造函数Task(Action action),因此您实际上实际上并没有等待异步方法。如果您检查 real C# 而不使用 asyn-await 语法糖,您将看到 AsyncVoidMethodBuilder。对于Task.Run,您使用Task.Run(Func&lt;Task&gt; func),因此您会收到“真实”任务。

    所以如果你想使用构造函数,你应该使用 cmets 的方法:new Task&lt;Task&gt;

    【讨论】:

    • 这如何回答这个问题?
    【解决方案4】:

    检查一下

    public static async Task Main(string[] args)
    {
        var t = Task.Factory.StartNew(async () => await AsyncTest());
        //t.Start();
        t.Result.Wait();    
        t.Wait();
        Console.WriteLine("Main finished");
    }
    
    private static async Task AsyncTest()
    {
        //Thread.Sleep(2000);
        await Task.Delay(2000);
        Console.WriteLine("Method finished");
    }
    

    还有这个link

    【讨论】:

    • 问题是关于:为什么模式var t = new Task(...); t.Wait(); 不能按预期工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-09
    • 2023-03-10
    • 2020-10-04
    • 2013-02-15
    • 1970-01-01
    • 1970-01-01
    • 2021-06-27
    相关资源
    最近更新 更多