Task.Wait不等待异步方法完成

时间:2019-06-04 09:51:20

标签: c# .net asynchronous

代码如下:

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()。但是,问题是为什么它以这种奇怪的方式工作?

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

UPD:将var t = new Task(async () => await AsyncTest())重写为var t = new Task(()=> AsyncTest().Wait())也解决了该问题。尽管我还是不太明白为什么Task无法在委托中使用async / await正常工作。

4 个答案:

答案 0 :(得分:6)

  

我仍然不太明白为什么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.Task可以很好地替代Task.Run,可以理解async lambda。

  

在我的真实程序中,任务已推迟执行。在一个位置创建任务对象,然后在程序的另一部分执行特定条件之后再创建。

在这种情况下,请使用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");
}

答案 1 :(得分:0)

@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");
}

答案 2 :(得分:-2)

选中此

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

答案 3 :(得分:-4)

请勿使用var task = new Task(...); task.Start();,而应仅使用Task.Run

在第一种情况下,使用构造函数Task(Action action),因此实际上您实际上并不在等待异步方法。如果您在不使用asyn-await语法糖的情况下检查real C#,则会看到AsyncVoidMethodBuilder。如果使用Task.Run,则使用Task.Run(Func<Task> func),以便收到“实际”任务。

因此,如果要使用构造函数,则应使用注释中的方法:new Task<Task>