如何使用async/await在UI线程上异步运行多个任务?

And*_*eas 9 .net c# asynchronous threadpool async-await

我已经阅读(和使用过)async/await一段时间了很长一段时间但我还有一个问题我无法得到答案.说我有这个代码.

private async void workAsyncBtn_Click(object sender, EventArgs e)
{
    var myTask = _asyncAwaitExcamples.DoHeavyWorkAsync(5);
    await myTask;
    statusTextBox.Text += "\r\n DoHeavyWorkAsync message";
}
Run Code Online (Sandbox Code Playgroud)

它从UI线程调用并返回到UI线程.因此,我可以在此方法中执行UI特定的操作await myTask.如果我曾经使用过,.ConfigureAwait(false)我会在做的时候得到一个线程异常,statusTextBox.Text += "\r\n DoHeavyWorkAsync message";因为我可以通过telled myTask来接受来自线程池的任何可用线程.

我的问题.据我所知,在这种情况下我永远不会离开UI线程,它仍然是异步运行,UI仍然是响应式的,我可以同时启动多个任务,从而加快我的应用程序.如果我们只使用一个线程,它怎么能工作?

谢谢!

编辑Sievajet

private async void workAsyncBtn_Click(object sender, EventArgs e)
{
    await DoAsync();
}

private async Task DoAsync()
{
    await Task.Delay(200);
    statusTextBox.Text += "Call to form";
    await Task.Delay(200);
}
Run Code Online (Sandbox Code Playgroud)

Yuv*_*kov 10

据我所知,在这种情况下我永远不会离开UI线程,它仍然是异步运行,UI仍然是响应式的,我可以同时启动多个任务,从而加快我的应用程序.如果我们只使用一个线程,它怎么能工作?

首先,我建议阅读Stephan Clearys博客文章 - 没有线程.

为了理解如何完全运行多个工作单元,我们需要掌握一个重要的事实:异步IO绑定操作(几乎)与线程无关.

怎么可能?好吧,如果我们一直深入到操作系统,我们会看到对设备驱动程序的调用- 那些负责执行网络调用和写入磁盘操作的设备驱动程序都是自然异步实现的,他们在做工作时不会占用一个线索.这样,当设备驱动程序正在做它的事情时,不需要一个线程.只有当设备驱动程序完成其执行时,它才会通过IOCP(I/O完成端口)向操作系统发出信号,然后执行方法调用的其余部分(这是通过线程池在.NET中完成的,它有专门的IOCP线程).

Stephans博客文章很好地证明了这一点:

走下异步的兔子洞

一旦操作系统执行DPC(延迟过程调用)并对IRP(I/O请求包)进行排队,它的工作基本上完成,直到设备驱动程序用我完成的消息发回信号,这导致整个操作链(在博客文章中描述)要执行,最终最终会调用你的代码.

需要注意的另一件事是.NET在使用async-await模式时在幕后为我们做了一些"魔术" .有一个叫做"同步上下文"的东西(你可以在这里找到一个相当冗长的解释).这个同步上下文是负责await再次调用UI线程上的继续(第一个之后的代码)(在存在这种上下文的地方).

编辑:

应该注意的是,同步上下文的魔力也适用于CPU绑定操作(实际上对于任何等待的对象),所以当你使用线程池线程时,Task.Run或者Task.Factory.StartNew,这也可以.