dotnet 使用 ConfigureAwait.Fody 库设置默认的 await 同步上下文切换配置
在 dotnet 里面,使用 await 进行异步逻辑,默认是会尝试切换回调用 await 的线程同步上下文。这个机制对于大多数的上层应用来说都是符合逻辑且方便的逻辑,例如对于带 UI 线程的 WPF 或 WinForms 等应用,基础开发的执行逻辑基本都是在 UI 线程上,此时进入一次 await 再出来,期望如果是进入 await 之前是在 UI 线程,那么执行 await 完成之后,退出的代码也能在 UI 线程执行,正好这就是 dotnet 的默认行为。但是对于库开发者来说,情况就反过来的,库的开发者大部分时候更期望默认不要切换回调用方的线程,采用 Fody 的 ConfigureAwait.Fody 库,可以控制此默认的行为。本文将告诉大家如何使用 ConfigureAwait.Fody 库
这是一个在 GitHub 上使用最友好的 MIT 协议开源的库,请看 https://github.com/Fody/ConfigureAwait
用 Fody 的 ConfigureAwait.Fody 库,可以控制 await 在结束之后的切换同步上下文默认的行为,对于库的开发者来说相对比较方便。大部分的库的逻辑,都是期望在异步之后,不要明确切换回原调用方的线程,因为切换回原调用方的线程存在很多不可控逻辑。例如在 WPF 里面,需要通过 Dispatcher 调度,如此会让 UI 线程过于繁忙。而且切换调度逻辑,可能出现和原有线程相互等待的情况
例如 UI 线程进入了 Wait 逻辑,等待异步执行完成。然而异步执行完成的最后一步是做切换线程同步上下文,切换到 UI 线程。大家可以看到,异步的最后一步是在等待 UI 线程切换,相当于在 WPF 里面使用 Dispatcher 调度,然而 UI 线程却进入了 Wait 方法,也就是 UI 线程在异步完成之后无法进行调度。此时的异步在等待 UI 调度,而 UI 在等待异步完成。如此将会锁住 UI 线程
详细请看
根据 walterlv 大佬的 在编写异步方法时,使用 ConfigureAwait(false) 避免使用者死锁 - walterlv 博客,可以了解到,在库里面,如果不关心线程本身,例如代码不需要在 WPF 的 UI 线程执行,可以采用 ConfigureAwait(false) 的方式避免使用者死锁
原因在于在 await 完成前,可以采用 ConfigureAwait 配置异步的最后一步是否需要尝试切换回原有的线程。默认是 true 的值,表示需要。如果加上了 ConfigureAwait 函数,设置 false 的值,那就表示不要切换回原有的线程。此时如果业务端在 UI 线程使用 Wait 等方法,那依然是安全的,原因是 UI 线程在等待异步完成,然而异步完成不需要调度回 UI 线程,可以由线程池选择线程调度,于是异步的完成不需要等待 UI 线程,能够让 UI 线程等待异步完成
那引入的问题就来了,在库里面,将会让 await 异步逻辑充满了 ConfigureAwait(false) 的代码,如此将会让代码不好看。用 Fody 的 ConfigureAwait.Fody 库就是用来解决此问题的,可以配置默认行为,例如 dotnet 里面默认是用的是 true 的值,对于库的代码,可以反过来,配置默认是 false 的值,可以减少大量的代码
按照 dotnet 的使用惯例,第一步就是先安装 NuGet 库。由于 ConfigureAwait.Fody 库是 Fody 库的扩展,请同时安装 ConfigureAwait.Fody 和 Fody 库
使用方法很灵活,可以配置整个程序集的默认行为,也可以只配置某个类或类里面某个方法的默认行为。配置整个程序集的默认行为代码如下,添加程序集的特性即可
[assembly: Fody.ConfigureAwait(false)]
对于某个类或类里面某个方法的默认行为的配置,可以给类或方法加上如下特性
[Fody.ConfigureAwait(false)]
例子如下
[Fody.ConfigureAwait(false)]
private async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Debug.WriteLine($"ThreadId={Thread.CurrentThread.ManagedThreadId}"); // 输出 1
await Task.Delay(100);
Debug.WriteLine($"ThreadId={Thread.CurrentThread.ManagedThreadId}"); // 输出 2
}
如此即可配置行为为加上 ConfigureAwait(false) 不尝试切换回原因的线程同步上下文
按照 Fody 的使用方法,加上 FodyWeavers.xml 文件,在 FodyWeavers.xml 文件里面开启 ConfigureAwait.Fody 的功能
<Weavers>
<ConfigureAwait/>
</Weavers>
如果想对程序集做默认配置,也可以不写程序集特性,可以通过在 FodyWeavers.xml 文件里设置默认值的方式实现
<Weavers>
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>
实现原理是编译器优化,如原本的代码如下
using Fody;
[ConfigureAwait(false)]
public class MyAsyncLibrary
{
public async Task MyMethodAsync()
{
await Task.Delay(10);
await Task.Delay(20);
}
public async Task AnotherMethodAsync()
{
await Task.Delay(30);
}
}
将会编译生成大概如下等价代码
public class MyAsyncLibrary
{
public async Task MyMethodAsync()
{
await Task.Delay(10).ConfigureAwait(false);
await Task.Delay(20).ConfigureAwait(false);
}
public async Task AnotherMethodAsync()
{
await Task.Delay(30).ConfigureAwait(false);
}
}
相当于不需要开发者手动加上 ConfigureAwait 方法,通过此工具自动加上
dotnet 使用 ConfigureAwait.Fody 库设置默认的 await 同步上下文切换配置的更多相关文章
- IIS设置默认主页无效
服务器系统:Windows server 2008 R2 IIS版本:7.5 IIS中部署一个dotnet framework 3.5的网站应用程序,设置"默认文档"为:index ...
- 实例讲解Oracle数据库设置默认表空间问题
实例讲解Oracle数据库设置默认表空间问题 实例讲解Oracle数据库设置默认表空间问题,阅读实例讲解Oracle数据库设置默认表空间问题,DBA们经常会遇到一个这样令人头疼的问题:不知道谁在O ...
- 我的Android进阶之旅------>Android 设置默认语言、默认时区
1. 设置默认时区 PRODUCT_PROPERTY_OVERRIDES += \ persist.sys.timezone=Asia/Shanghai\ 注:搜索“persist.sys.timez ...
- 设置默认Browser
电信A库要求android系统中有多个Browser时,开机自动设置一个默认浏览器,而不用弹出选择框让用户手动选择. 监听开机广播Intent.ACTION_BOOT_COMPLETED, 用Pack ...
- 微软更新导致的IIS7设置默认主页无效
近期两个superKM的老客户出现问题,网站不能自动检索默认文档,必须通过完整网址才能访问. 值得一提的是出现问题的都是 IIS7 和7.5版本,服务器为windows server2008 R2. ...
- MTK Android中设置默认时区
设置默认时区 PRODUCT_PROPERTY_OVERRIDES += \ persist.sys.timezone=Asia/Shanghai\ 注:搜索“persist.sys.timezone ...
- datepickerx设置默认日期
datepicher插件是jQuery UI的一个插件,它提供一个日期弹出窗口(或直接显示在页面),供用户选择日期.在Web开发中,总会遇到需要用户输入日期的情况.一般都是提供一个text类型的inp ...
- .NET DateTime类型变量作为参数时设置默认值
一个小的 Tips. .NET 中函数参数的默认值需要是编译时常量.如果参数是引用类型,可以设置Null,如果是值类型,可以设置相应的编译时常量,如整型可以用整数,但对于DateTime(结构体,值类 ...
- ng-option指令使用记录,设置默认值需要注意
ng-options一般有以下用法: 数组作为数据源: label for value in array select as label for value in array label group ...
- 《Entity Framework 6 Recipes》中文翻译系列 (14) -----第三章 查询之查询中设置默认值和存储过程返回多结果集
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-6在查询中设置默认值 问题 你有这样一个用例,当查询返回null值时,给相应属性 ...
随机推荐
- 没想到三天10KStar的营销利器MediaCrawler开源作者已经删库了
前言 一站式社交平台数据抓取利器,带你玩转小红书.抖音.快手.B站和微博数据分析 不经意间,来查看MediaCrawler仓库源码,发现作者已经删库了.看来是领奖了.才几天不到的时间Star数量已经直 ...
- 记录--vue3函数式弹窗
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前言 最近接到一个需求,需要在一些敏感操作进行前要求输入账号和密码,然后将输入的账号和密码加到接口请求的header里面.如果每个页面都去 ...
- Prompt工程全攻略:15+Prompt框架一网打尽(BROKE、COAST、LangGPT)、学会提示词让大模型更高效
Prompt工程全攻略:15+Prompt框架一网打尽(BROKE.COAST.LangGPT).学会提示词让大模型更高效 0.相关文章推荐 更多Prompt框架技术细节和原理见相关文章 Prompt ...
- 小师妹学JavaIO之:文件读取那些事
目录 简介 字符和字节 按字符读取的方式 按字节读取的方式 寻找出错的行数 总结 简介 小师妹最新对java IO中的reader和stream产生了一点点困惑,不知道到底该用哪一个才对,怎么读取文件 ...
- OpenHarmony创新赛丨报名倒计时,超强秘籍带你直通大奖!
OpenHarmony创新赛报名倒计时开始啦! 设于开放原子全球开源大赛下的OpenHarmony创新赛,目前正在如火如荼地进行赛事招募中!这次大赛围绕创新应用.商显行业.金融行业三大赛题,邀请来 ...
- C 语言文件处理全攻略:创建、写入、追加操作解析
C 语言中的文件处理 在 C 语言中,您可以通过声明类型为 FILE 的指针,并使用 fopen() 函数来创建.打开.读取和写入文件: FILE *fptr; fptr = fopen(filena ...
- Go 语言数组基础教程 - 数组的声明、初始化和使用方法
数组用于在单个变量中存储相同类型的多个值,而不是为每个值声明单独的变量. 声明数组 在Go中,有两种声明数组的方式: 使用var关键字: 语法 var array_name = [length]dat ...
- Aspose.Cells使用总结大全
引用:https://blog.csdn.net/u011555996/article/details/79000270 使用到 Aspose.Cells 插件,整理一下. 一:新建解决方案,目录如下 ...
- Docker 12 Dockerfile
简介 Dockerfile 是用来构建 Docker 镜像的文件,可以理解为命令参数脚本. Dockerfile 是面向开发的,想要打包项目,就要编写 Dockerfile 文件. 由于 Docker ...
- 【FAQ】HarmonyOS SDK 闭源开放能力 —Map Kit
1.问题描述 在App中供用户在地图上选择地址,目前在使用Map Kit结合geolocationManager逆地理编码时获取的地址信息描述不准确,希望能提供相应的Demo参考. 解决方案 Demo ...