async/wait和ConfigureWait(false)在异步编程中有什么关系?
分类: .Net技术 ◆ 标签: #异步编程 #基础 #.Net ◆ 发布于: 2025-02-17 17:50:47

我喜欢.Net
的最大原因就是.Net
是真的提供了很多工具简化编程的难度,并提供了健壮性。尤其是异步编程模式的提出。
.Net
使用async
和await
两个关键字来简化异步编程,不过需要注意的是:
异步编程不是并行编程,和我们提到的多线程编程虽然有联系,但是异步编程不是多线程的那种多任务编程。
异步编程要注意编程任务是基于
CPU
计算多,还是基于IO
多,特别是在基于Asp.net Core Genric Host
进行后端服务编程时(例如:写基于Windows或者基于Linux的服务程序,不是指Web
编程),特别需要注意区分你的任务是基于IO
的还是基于CPU
计算的任务。基于CPU
计算的任务都需要Task.Factory.new
放入runtime
的线程池中运行。在基础的编程模型中我们有多进程,多线程分别利用的是CPU的特性,例如分时CPU,多核等等,但是还有一个概念就是可阻塞IO的模型,可以将异步编程模型的基础理念架构在可阻塞IO上。
.Net
的异步编程实际上使用起来也很简单:
public async Task<T> dosomethingasync(otherParameters )
{
await doanothermethodinAsync();
}
使用关键字async/await
成对的来定义一个异步方法。需要注意的是必须是成对的出现,假如在方法定义使用了async
, 但是方法体并没有使用await来等待Task
,那么这个方法就会转为一个同步方法。
很多人不理解:定义了一个异步方法,然后在方法体内使用await
等待返回,await
之后的代码必须在await
的对象返回之后才会开始执行,这怎么就实现了异步了。实际是要从更高的角度去理解,假如在Asp.net Core
或者是UI
线程里单独执行的时候,类似这样:
//这个时候已经进入了asp.net core或者UI的主线程了。
var task1 = doMethod1Async();
var task2 = doMethod2Async();
var task3 = doMethod3Async();
//do anohter things.
上面这部分代码是在一个单一的线程里运行。如果是在同步的模式下,肯定是需要先调用完方法1,再方法2,方法3.
现在这三个方法都定义为异步方法(记住异步方法的要点,方法体内必须要调用await
), 例如方法1:
public async Task<string> doMethod1Async()
{
var result = "test";
result = await doSomeMethodReturnString();
return result;
}
当在主线程里执行到var task1 = doMethod1Async()
时会进入方法1,在方法体内,运行到result = await doSomeMethodReturnString()
时,主线程会立即返回,并由runtime
保留对于调用方法doMethod1Async
的上下文,并继续执行,主线程跟着就开始执行方法2。
所以异步主要就是指这种情况。
那么在主线程如何保证三个task都会执行完呢?
await Task.WhenAll(task1, task2, task3);
//或者
await Task.WhenAny(task1, task2, task3);
这个是理解异步编程的主要知识点。
另外对于异步方法的返回值,Task
和Task<T>
, 只有事件是返回void
。
async/await
和ConfigureAwait()
方法之间的关系
我们上面有谈到,当runtime
在调用异步方法的时候,会创建State Machine
并保存当时的上下文,例如Asp.net Core
的HttpContext
, 在UI
编程时的同步上下文,当一个异步方法运行完成之后,默认情况下是要返回当时的线程的,使用方法ConfigureAwait(false)
表示某个Task
在运行完成之后不用返回当时的线程,返回时有什么线程就用什么线程,甚至新起一个线程。对于大多数情况我们都建议使用ConfigureAwait(false)
, 防止死锁,特别是在同步环境下调用异步方法的情况下,更明显。
注意
在如今的Asp.net Core
框架中构建代码可以不使用这个方法, 框架处理郭类似的问题,但是UI
或者Console
应用中强烈建议使用。