异步/示例等待

时间:2019-04-12 02:07:42

标签: c# multithreading asynchronous .net-core async-await

好吧,我想我几乎可以理解async / await和多线程上的所有绒毛了。我知道异步与任务有关,多线程与工作人员有关。这样一来,您就可以在同一线程上运行不同的任务(this answer在解释它方面做得很好)。

所以我制作了一个小程序以查看魔术的发生,但我有些困惑:

public class Program
{
    public static async Task Main(string[] args)
    {
        var task = ToastAsync();
        Console.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] Cleaning the kitchen...");
        await task;
        Console.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] Take the toast");
    }

    public async static Task ToastAsync()
    {
        Console.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] Putting the toast");
        Console.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] Setting a timer");
        await Task.Delay(2000);
        Console.WriteLine($"[{Thread.CurrentThread.ManagedThreadId}] Toast is ready");
    }
}

在第一次运行该程序之前,我希望它可以在单个线程上运行。就像我上面链接的答案一样,我希望这相当于“在烤面包机的计时器运行时打扫厨房”。结果虽然矛盾:

[1] Putting the toast
[1] Setting a timer
[1] Cleaning the kitchen...
[4] Toast is ready
[4] Take the toast

上面的结果对我来说意义不大。到底发生了什么事?似乎该功能的一部分正在一个线程中执行,然后当它到达await点时,它将执行权交给另一个线程...?我什至不知道这是可能的D:

此外,我对上面的示例进行了一些更改。在主函数中,我使用await task;而不是task.Wait()。然后结果改变了:

[1] Putting the toast
[1] Setting a timer
[1] Cleaning the kitchen...
[4] Toast is ready
[1] Take the toast

现在,这看起来更像示例。就像烤面包机上的计时器充当了另一个“炊具”一样。但是,为什么与使用await不同?有没有办法在单个线程中完全获取异步任务?我的意思是,在Toast is ready上也有thread 1吗?

感谢异步!

2 个答案:

答案 0 :(得分:5)

需要注意的是

  1. 调用ToastAsync时,它会启动 任务 (即使您没有调用{{ 1}})。注意:启动任务并不意味着启动线程...这反过来解释了为什么要“清洁厨房”。在“放烤面包”的后面)

  2. await方法将一直运行到击中其第一个async关键字为止,并将控制权交还给调用者。

  3. 由于控制台应用中没有await。编译器认为无需在调用线程上创建 Continuation ,这反过来解释了为什么“ Take the toast”与“ Toast ready”在同一线程上

注意 :如果您在弄乱后打扫厨房,则不必事先打扫

来自评论

  

然后我知道,等待将始终从线程中获取线程   池运行该方法(这确实让我大吃一惊)   基本上不可能有一个异步/等待单线程

Asnyc and Await模式本身不是关于多线程的问题,而是关于可伸缩性和与IO绑定的工作负载和/或 UI 响应能力(在< em> UI 框架)

看看有关这个主题的 Stephen Cleary的博客

Async and Await - Stephen Cleary

答案 1 :(得分:1)

如果您从Windows窗体事件中运行相同的代码,则会得到预期的结果:

[1] Putting the toast
[1] Setting a timer
[1] Cleaning the kitchen...
[1] Toast is ready
[1] Take the toast

这是因为默认情况下,await在等待之后恢复上下文,但是这样做是有代价的,因为这是一件额外的事情。如果不需要上下文还原,则可以这样配置await:

await Task.Delay(2000).ConfigureAwait(false);

...,您将看到与控制台应用程序相同的输出。

请记住,等待不是要同时执行操作,而是要使UI保持响应。您希望能够继续接收新的烤面包订单,而以前的订单正在烤面包。