异步和等待不起作用

5

我正在尝试学习.NET 4.5的异步和等待功能。首先,这是我的代码:

    static async void Method()
    {
        await Task.Run(new Action(DoSomeProcess));
        Console.WriteLine("All Methods have been executed");
    }

    static void DoSomeProcess()
    {
        System.Threading.Thread.Sleep(3000);
    }

    static void Main(string[] args)
    {
        Method();
        //Console.WriteLine("Method Started");
        Console.ReadKey();
    }

这段代码在控制台上没有输出任何结果。我不明白为什么。我的意思是任务不应该只是不阻塞的线程吗? 但是,如果我取消注释主方法中的Console.WriteLine(),似乎一切都正常了。

有人能告诉我这里发生了什么吗?


1
这对我来说正常工作,它会在3秒后将“所有方法已执行”打印到控制台。 - Sriram Sakthivel
除非我在主函数中取消注释Console.WriteLine,否则在3秒后控制台上什么也不会输出。 - Win Coder
通常情况下,应避免使用async void方法,因为无法等待其完成。但在这种特定情况下,您的代码将正常工作(除非在睡眠完成之前按下Enter键)。 - svick
取消注释也可以正常工作 :) - Sriram Sakthivel
我在Method()的WriteLine上插入了一个断点,线程似乎到达了那里,但由于某种原因没有在控制台上显示。为什么会发生这种情况? - Win Coder
显示剩余5条评论
2个回答

13

使用异步/等待模式时,有些事情与以前的线程不同。

  1. 不应该使用System.Threading.Thread.Sleep,因为它仍然是阻塞的,并且无法与异步一起使用。相反,请使用Task.Delay

  2. 考虑使您的所有代码都是异步的。只有控制台中的主方法不能是异步的

  3. 避免使用async void方法。基本上,async void仅适用于无法返回内容的事件处理程序。所有其他异步方法都应返回TaskTask<T>

修改了您的示例:

    static async Task Method()
    {
        await DoSomeProcess();
        Console.WriteLine("All Methods have been executed");
    }

    static async Task DoSomeProcess()
    {
        await Task.Delay(3000);
    }

现在更改您的Main方法,因为这应该是您启动任务的地方。

    Task.Run(() => Method()).Wait();
    //Console.WriteLine("Method Started");
    Console.ReadKey();

1
为什么需要调用 Task.Run - Paulo Morgado
1
英雄,Task.Run(() => Method()).Wait(); 完美运行。 - Grenter

4
学习如何正确使用asyncawait关键字需要一些学习曲线。
问题在于等待的人没有等待,还有一些其他细节,例如SyncronizationContextTask。您应该查阅一些文章:http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx 要在控制台中使用asyncawait关键字,您需要额外的代码:Can't specify the 'async' modifier on the 'Main' method of a console appAwait a Async Void method call for unit testingUnit Test Explorer does not show up Async Unit Tests for metro apps
我通常采用以下方法:
static void Main(string[] args)
{
    Console.WriteLine("HELLO WORLD");
    var t1 = Task.Factory.StartNew(new Func<Task>(async () => await Method()))
        .Unwrap();
    Console.WriteLine("STARTED");
    t1.Wait();
    Console.WriteLine("COMPLETED");
    Console.ReadKey();
}

static async Task Method()
{
    // this method is perfectly safe to use async / await keywords
    Console.WriteLine("BEFORE DELAY");
    await Task.Delay(1000);
    Console.WriteLine("AFTER DELAY");
}

你为什么要创建另一个任务?Method返回的那个不够好吗? - Paulo Morgado
1
我需要封装一个任务,以便使用它的同步上下文。如果不这样做,根据当前上下文,"完成"文本可能会在"After Delay"之前显示。 - J. Lennon
@J.Lennon,在控制台应用程序中没有SynchronizationContext。即使有,使用SynchronizationContext的是编译器生成的状态机,当使用async/await时。 - Paulo Morgado
@J.Lennon,你的异步匿名方法没有做任何事情。如果你直接将Method作为委托放入StartNew中,这个例子会做完全相同的事情。请注意,不应该使用StartNew与异步方法。你应该使用Task.Run,因为它会自动展开返回的任务。目前你没有手动这样做,所以当t1完成时,它只是表示它已经完成了启动其他任务,而不是完成了内部任务。 - Servy
3
@J.Lennon,“Unwrap”关闭后需要向右滚动才能看到。 在这种情况下,它是屏幕上唯一未显示的内容,所以我没有注意到它。 话虽如此,你的“async”匿名方法仍然是无意义的。 如果你只是写了“t1 = Method();”,那么代码会更简单、可读且有效。 - Servy
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接