深入理解c#异步函数

来源:互联网 发布:illustrator cs6 mac 编辑:程序博客网 时间:2024/06/08 13:57

在之前的博客c# async await中,我演示了如何使用async和await来实现异步编程。本篇博客来讨论async和await是如何工作的。

假设有一个asp.net的接口,接收到参数后需要请求数据库,然后将结果返回,数据库操作耗时1秒钟。
当某个时间点同时有100个请求,这些请求会入队列等待资源池的调度,然后资源池分配若干个线程来处理队列中的任务,这些线程执行后开始请求数据库,由于数据库操作耗时1秒,所以这些线程都会被阻塞1秒钟,阻塞的时候导致CPU闲置,并且会导致线程池创建更多的线程来处理队列中剩下的请求,当然这些线程也很快被阻塞。当数据库操作返回后,被阻塞的线程被唤醒,又会发生上下文切换。好在资源池比较聪明,当多余的线程回到资源池以后,线程池不会调度他们去处理新的任务,并且在某个时间点回收掉他们的资源。

这就是同步方法的执行流程。它会导致创建更多的线程,更多的线程需要占用更多的资源,并且会进行更多的上下文切换。

如果使用异步函数async和await的话,情况就会得到改善。资源池分配若干个线程来处理队列中的任务,这些线程调用异步方法来请求数据库,在数据库操作结束之前,当前方法会直接返回,当前线程就回到了资源池,可以用来处理其它任务。当数据库操作完成后,也会将操作结果放入线程池的队列中,线程池分配线程来处理这些任务时,会从上次await的地方开始继续执行,执行完毕之后,接口返回结果,当前线程又回到了线程池。
异步函数的执行流程中没有阻塞,提高了线程的利用率,也提高了接口的吞吐量。

来看以下代码

void myfunction(){    func1();    await doSomethingAsync();    func2();}

上面的代码虽然在一个方法中,但实际运行的时候,await之前和await之后的代码可能会在两个不同的线程中执行,编译器会生成一个状态机代码,包含两个状态,await之前和之后分别是一个状态。doSomethingAsync()作为一个异步方法,返回一个TaskTask<Result>,编译器会调用Task.ContinueWith()方法来恢复状态机而执行await之后的代码。

原创粉丝点击