谁在 Async Await 上下文中运行 awaitable

分享于2022年07月17日 .net .net-core async-await c# threadpool 问答
【问题标题】:谁在 Async Await 上下文中运行 awaitable(Who runs the awaitable in an Async Await context)
【发布时间】:2022-01-27 21:10:41
【问题描述】:

我一直在阅读,我注意到 Async/Await 在 I/O 或任何其他硬件组件上运行时不一定会创建新线程。

但是当操作是 CPU Bound 时会发生什么?

    public partial class Form1 : Form
    {
        private static List _logs = new List();

        public Form1()
        {
            Print("Form constructor");
            InitializeComponent();
        }

        private async void button1_Click(object sender, EventArgs e)
        {
            Print("Caller");
            await SomeLongOperation();
        }

        private async Task SomeLongOperation()
        {
            Print("Called");
            await Task.Delay(10000);
            Print("Called after task");
        }

        public void Print(string txt)
        {
            _logs.Add($"I'm writing from thread {Thread.CurrentThread.ManagedThreadId}. And, {txt}");
        }
    }

假设我有这个类,如果主线程调用button1_Click方法运行到awaitable,然后该方法的其余部分在消息循环中排队,从而让主线程继续工作,那么谁正在运行 awaitable,因为主线程已经在 UI 上工作并等待 awaitable 完成。

  • 线程已经在 UI 上工作并等待可等待对象 那里没有等待
  • 所以你基本上是在问 await Task.Delay(10000); 在哪里运行。您是否认为此特定操作受 CPU 限制?如果是,为什么?
  • 使用调试器即可。调试 > 新断点 > 函数断点并键入“Task.Delay”。运行程序,当它中断时查看 Debug > Windows > Call stack。这很无聊,更令人费解的是它如何在延迟调用后恢复运行。在 Print 语句上设置断点。使用参考源来理解你所看到的,源代码中的 cmets 有点启发性。
  • “Task.Delay 在哪里运行” - There is no thread
  • @MatthewAlmeida Task.Delay 不是 CPU 操作。

【解决方案1】:

我认为您将“异步”与“并行”混淆了。

  • 异步意味着线程在等待其他事情时被释放以执行其他事情。这是关于您的代码如何 等待
  • 并行是指代码的两个部分同时运行。这只能通过多个线程来完成。这是关于您的代码如何 运行

您的示例代码是异步的,但不是并行的。一切都在一个线程上完成。 Task.Delay 不是 CPU 操作。线程没有什么可做的。 There is no thread .

如果您确实需要运行一些 CPU 密集型操作,您可以使用 Task.Run 将其卸载到另一个线程。例如:

await Task.Run(() => DoSomethingIntensive());

这混合了异步与并行的概念。 DoSomethingIntensive() 在单独的线程上运行。那是平行的。但是主线程异步等待,这意味着它可以自由地去做其他事情(比如响应用户输入),同时等待其他线程做它的事情。