在转移到与调用者相同的线程之前,是否在内部创建另一个线程(对于UI应用程序)

本文关键字:线程 创建 在内部 是否 另一个 应用程序 UI 对于 转移 调用者 | 更新日期: 2023-09-27 18:07:13

我对async/await的了解是,当任务完成时,在调用await时,在相同的上下文中运行延续,在我的情况下,这将是UI线程。但我的问题是,它是否创建一个新的线程(内部)IO完成后,移动到相同的UI线程之前。

我正在分享一段代码。如果我点击这个按钮一次,在执行await之前,它显示可用线程是1023,但在那之后,可用线程下降到1022。虽然当我检查线程id时,它与UI线程相同。

    private async void button1_ClickAsync(object sender, EventArgs e)
    {
        int x, y;
        ThreadPool.GetAvailableThreads(out x, out y);
        textBox1.Text = x.ToString()+"..."+y.ToString();
        await Task.Delay(5000);
        ThreadPool.GetAvailableThreads(out x, out y);
        textBox1.Text = x.ToString() + "..." + y.ToString();
    }
但有趣的是,下次当我点击这个按钮时,可用线程的数量仍然是1023(等待之前和之后)。

在转移到与调用者相同的线程之前,是否在内部创建另一个线程(对于UI应用程序)

但我的问题是,它是否创建一个新的线程(内部)IO完成后,移动到相同的UI线程之前。

其他线程可能会被临时使用,但您不需要担心。

特别是,. net上的I/O通常通过I/O完成端口,该端口是线程池的一部分。根据需要自动添加和删除I/O线程。通常,I/O在真正准备返回到代码之前(例如,解析HTTP响应头)还有一些额外的工作要做,所以BCL I/O代码的lot实际上使用I/O线程只是为了将工作排队到线程池。因此,线程池工作线程经常被I/O代码(短暂地)使用。

同样,在这个特殊的例子中,我相信还有一个单独的计时器线程,它合并了系统计时器。当然,这是一个实现细节,可能会发生变化。

因此,总的来说,其他线程可能会被创建/销毁/临时使用,但您不必担心。它们都由BCL或。net运行时以一种非常有效的方式进行管理,在重用线程(尽量减少流失)和尽量减少资源使用(特别是内存)之间取得平衡。

我猜你的意思是降到1022。

一般来说,我认为这取决于正在进行的异步调用。磁盘和网络调用将从I/O完成线程池返回到线程上。似乎Task.Delay返回一个普通的工作线程。

您可以将行更改为await Task.Delay(5000).ConfigureAwait(false);,在它之后设置一个断点,并检查线程窗口以直接查看。

无论你从哪里调用它,它都将在工作线程上完成。await不将调用上下文传递给实现异步操作的函数;它只增加了在完成后返回UI线程的额外步骤。

我不会在这里过多地追踪确切的数字;CLR有自己的算法来管理线程池的大小,这些算法可以在不同的版本之间改变。我不会强调它在这里使用不同的线程:在一个正常的应用程序中,它会简单地从池中重用一个现有的线程,操作将非常快。