在数据访问层(DAL)使用ConfigureAwait(false)的相关技术疑问
关于
ConfigureAwait(false)的技术疑问 有文章讲解Dapper用法时,在ExecuteAsync章节给出如下示例代码:
public async Task<int> UpdateSalesOrder(int salesOrderId, byte status) { const string sql = @" UPDATE Sales.SalesOrderHeader SET STATUS = @status WHERE SalesOrderID = @salesOrderId"; var param = new {status, salesOrderId}; using var conn = _db.GetAdventureWorksConnection(); return await conn.ExecuteAsync(sql, param) .ConfigureAwait(false); }
并给出建议:
最好在数据访问层取消与主线程的任何同步,因为这会影响可扩展性。因此,设置ConfigureAwait(false)是个好主意,这样就不会等待主线程。遗憾的是,C#不会自动为开发者完成此操作,因此需要养成习惯。一个不错的经验法则是:如果在DAL中工作,要在每个调用栈方法中都添加ConfigureAwait(false)。
针对此提出的三个技术疑问,解答如下:
1. 该建议是否仅适用于类似Dapper这种与数据库交互的代码?
不是,这个建议适用于所有不需要回到原始上下文的异步代码,不止数据库交互场景。只要你的异步方法不需要依赖调用时的上下文(比如UI线程的同步上下文、ASP.NET的请求上下文),都应该添加ConfigureAwait(false)。比如文件IO、网络请求、第三方API调用这类异步操作,只要方法本身不依赖上下文,加这个配置都能提升性能和扩展性,避免不必要的上下文切换和线程阻塞。
2. 能否说明C#会自动执行此操作的场景?
C#本身不会自动帮你添加ConfigureAwait(false),但有几种场景下,效果等同于自动应用了类似的行为:
- 在无同步上下文的环境执行异步代码:比如控制台程序、ASP.NET Core(默认无同步上下文),此时
await后的代码会直接在线程池线程上执行,不需要切换回原始上下文,和加了ConfigureAwait(false)的效果一致。 - 使用已完成的
ValueTask:如果ValueTask已经处于完成状态,await不会触发上下文切换,自然也就不需要ConfigureAwait(false)。 - 部分第三方异步库的内部实现:有些库的异步方法内部已经处理了上下文切换,对外暴露的API调用时,即使不加
ConfigureAwait(false),也不会回到原始上下文,但这是库内部的实现逻辑,并非C#自动处理。
3. 若此代码属于将被各类.NET应用引用的类库,会存在哪些影响?
如果类库中所有异步方法都添加了ConfigureAwait(false),对各类.NET应用的影响以正面为主,同时有几点需要注意:
- 正面影响:
- 提升类库的通用性和性能:不管调用方是有同步上下文的UI应用(WPF、WinForms),还是无上下文的后端服务(ASP.NET Core、控制台),都能避免不必要的上下文切换,减少线程阻塞,提升整体并发能力。
- 降低调用方的潜在风险:如果类库不加
ConfigureAwait(false),UI应用调用时可能因上下文切换导致死锁(比如在UI线程上同步等待异步方法),加了之后能大幅降低这种风险。
- 需要注意的点:
- 类库方法不能依赖原始上下文:如果类库中的方法需要访问调用方的上下文(比如UI线程的控件、ASP.NET的
HttpContext),加ConfigureAwait(false)会导致上下文丢失,引发错误。但数据访问层这类类库通常不需要依赖上下文,所以这个问题几乎不会出现。 - 调用方无需重复添加:如果类库已经加了
ConfigureAwait(false),调用方在调用类库方法时,除非自身有特殊的上下文需求,否则不需要再加,重复添加也不会有问题,但完全没必要。
- 类库方法不能依赖原始上下文:如果类库中的方法需要访问调用方的上下文(比如UI线程的控件、ASP.NET的
内容的提问来源于stack exchange,提问作者Codehelp
相关产品推荐
相关产品推荐

