如何UI线程上运行多个任务异步使用异步/等待工作?多个、线程、任务、工作

2023-09-03 11:31:13 作者:因为有你俄可以为所欲为。

我已经阅读(和使用)异步/计谋了不少有一段时间了,但我还有一个问题,我不能回答。说我有这个code。

 专用异步无效workAsyncBtn_Click(对象发件人,EventArgs的)
{
    变种MyTask的= _asyncAwaitExcamples.DoHeavyWorkAsync(5);
    等待MyTask的;
    statusTextBox.Text + =\ r \ñDoHeavyWorkAsync消息;
}
 

这就是所谓的从UI线程,并返回到UI线程。为此,我能够做的UI相关的东西在这个方法中,后等待MyTask的。如果我用了 .ConfigureAwait(假)这样做时,我会得到一个线程异常 statusTextBox.Text + =\ r \ñDoHeavyWorkAsync消息; ,因为我本来telled MyTask的这是确定采取任何可用线程的线程池。

我的问题。据我了解我永远不会离开UI线程在这种情况下,还是它的异步运行,用户界面​​仍然响应,我可以在同一时间启动多个任务,并为此加快我的申请。怎么能这样的工作,如果我们只使用一个线程?

谢谢!

编辑Sievajet

 专用异步无效workAsyncBtn_Click(对象发件人,EventArgs的)
{
    等待DoAsync();
}

私人异步任务DoAsync()
{
    等待Task.Delay(200);
    statusTextBox.Text + =叫成;
    等待Task.Delay(200);
}
 

解决方案   

据我所知,我从来没有离开UI线程在这种情况下,仍   它的异步运行,用户界面​​仍然响应,我可以启动   几个任务的同时,并为此加快了我的申请。   怎么能这样的工作,如果我们只使用一个线程?

首先,我建议你读斯蒂芬Clearys博客文章 - 没有线程。

VC 多线程工作笔记0003 MFC中用户界面线程的相关操作

为了了解如何它可以完全运行多个工作单元,我们需要把握的一个重要的事实:异步IO 绑定操作有(几乎的)没什么做线程。

这怎么可能?那么,如果我们一路钻深到操作系统中,我们将看到调用的的设备驱动程序的 - 那些在负责执行的操作,如网络电话,并写入磁盘,均实现自然同步的,他们不占用一个线程,在做他们的工作。通过这种方式,而设备驱动器正在做它的东西,有不必是一个线程。只有一次设备驱动程序执行完成,这将标志着操作系统,它的通过IOCP(I / O完成端口),这将执行方法调用的其余部分进行(这在.NET中通过线程池完成的,其中有专门的IOCP线程)。

Stephans博客中说明了这一点很好:

在OS运行DPC(推迟过程调用)和队列的IRP(I / O请求包),它的工作基本上完成,直到设备驱动器信号回用的我完成的消息,这将导致整个操作链(在博客文章中所述)来执行,这最终将结束与调用你的code。

另外要注意的是,.NET做一些魔术我们在幕后使用异步计谋模式时。有一个名为同步环境的东西(你可以找到一个相当长的解释here).该同步上下文是什么主管(之后的第一个计谋 code)在UI线程上回来调用的延续(在地方,这样的背景下存在)。

编辑:

应该注意的是,魔术与同步环境发生了CPU绑定操作,以及(实际上任何awaitable对象),所以当你使用通过 Task.Run 或 Task.Factory.StartNew ,这将正常工作。

I've read (and used) async/await quite a lot for some time now but I still have one question I can't get an answer to. Say I have this code.

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

It's called from the UI thread and returned to the UI Thread. Therefor I am able to do UI-specific things in this method and after the await myTask. If I had used .ConfigureAwait(false) I would get a thread exception when doing statusTextBox.Text += "\r\n DoHeavyWorkAsync message"; since I would have telled myTask it's ok to take any available thread from the thread pool.

My question. As I understand it I never leave the UI thread in this case, still it's run asynchronously, the UI is still responsive and I can start several Tasks at the same time and therefor speed up my application. How can this work if we only use one thread?

Thanks!

EDIT for 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);
}

解决方案

As I understand it I never leave the UI thread in this case, still it's run asynchronously, the UI is still responsive and I can start several Tasks at the same time and therefor speed up my application. How can this work if we only use one thread?

First, i'd recommend reading Stephan Clearys blog post - There is no thread.

In order to understand how its possible to run multiple units of work altogether, we need to grasp one important fact: async IO bound operations have (almost) nothing to do with threads.

How is that possible? well, if we drill deep down all the way to the operating system, we'll see that the calls to the device drivers - those which are in charge of doing operations such as network calls and writing to disk, were all implemented as naturally asynchronous, they don't occupy a thread while doing their work. That way, while the device driver is doing its thing, there need not be a thread. only once the device driver completes its execution, it will signal the operating system that it's done via an IOCP (I/O completion port), which will then execute the rest of the method call (this is done in .NET via the threadpool, which has dedicated IOCP threads).

Stephans blog post demonstrates this nicely:

Once the OS executes the DPC (Deferred Procedure Call) and queue the IRP (I/O Request Packet), it's work is essentially done until the device driver signals it back with the I'm done messages, which causes a whole chain of operations (described in the blog post) to execute, which eventually will end up with invoking your code.

Another thing to note is that .NET does some "magic" for us behind the scenes when using async-await pattern. There is a thing called "Synchronization Context" (you can find a rather lengthy explanation here). This sync context is whats in-charge of invoking the continuation (code after the first await) on the UI thread back again (in places where such context exists).

Edit:

It should be noted that the magic with the synchronization context happens for CPU bound operations as well (and actually for any awaitable object), so when you use a threadpool thread via Task.Run or Task.Factory.StartNew, this will work as well.