在 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.FodyFody

使用方法很灵活,可以配置整个程序集的默认行为,也可以只配置某个类或类里面某个方法的默认行为。配置整个程序集的默认行为代码如下,添加程序集的特性即可

[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 同步上下文切换配置的更多相关文章

  1. IIS设置默认主页无效

    服务器系统:Windows server 2008 R2 IIS版本:7.5 IIS中部署一个dotnet framework 3.5的网站应用程序,设置"默认文档"为:index ...

  2. 实例讲解Oracle数据库设置默认表空间问题

    实例讲解Oracle数据库设置默认表空间问题   实例讲解Oracle数据库设置默认表空间问题,阅读实例讲解Oracle数据库设置默认表空间问题,DBA们经常会遇到一个这样令人头疼的问题:不知道谁在O ...

  3. 我的Android进阶之旅------>Android 设置默认语言、默认时区

    1. 设置默认时区 PRODUCT_PROPERTY_OVERRIDES += \ persist.sys.timezone=Asia/Shanghai\ 注:搜索“persist.sys.timez ...

  4. 设置默认Browser

    电信A库要求android系统中有多个Browser时,开机自动设置一个默认浏览器,而不用弹出选择框让用户手动选择. 监听开机广播Intent.ACTION_BOOT_COMPLETED, 用Pack ...

  5. 微软更新导致的IIS7设置默认主页无效

    近期两个superKM的老客户出现问题,网站不能自动检索默认文档,必须通过完整网址才能访问. 值得一提的是出现问题的都是 IIS7 和7.5版本,服务器为windows server2008 R2. ...

  6. MTK Android中设置默认时区

    设置默认时区 PRODUCT_PROPERTY_OVERRIDES += \ persist.sys.timezone=Asia/Shanghai\ 注:搜索“persist.sys.timezone ...

  7. datepickerx设置默认日期

    datepicher插件是jQuery UI的一个插件,它提供一个日期弹出窗口(或直接显示在页面),供用户选择日期.在Web开发中,总会遇到需要用户输入日期的情况.一般都是提供一个text类型的inp ...

  8. .NET DateTime类型变量作为参数时设置默认值

    一个小的 Tips. .NET 中函数参数的默认值需要是编译时常量.如果参数是引用类型,可以设置Null,如果是值类型,可以设置相应的编译时常量,如整型可以用整数,但对于DateTime(结构体,值类 ...

  9. ng-option指令使用记录,设置默认值需要注意

    ng-options一般有以下用法: 数组作为数据源: label for value in array select as label for value in array label group ...

  10. 《Entity Framework 6 Recipes》中文翻译系列 (14) -----第三章 查询之查询中设置默认值和存储过程返回多结果集

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-6在查询中设置默认值 问题 你有这样一个用例,当查询返回null值时,给相应属性 ...

随机推荐

  1. ArcMap的mxd文件没有数据、显示感叹号怎么办?

      本文介绍在ArcMap软件中,导入.mxd地图文档文件后图层出现感叹号.地图显示空白等情况的解决办法.   在ArcMap软件使用过程中,我们经常会需要将包含有多个图层的.mxd地图文档文件导入软 ...

  2. DevOps迈向标准化,平台工程让开发运维更轻松

    在近一代人的时间里,DevOps 在软件开发和运维领域占据了主导地位.这是一套开发人员都离不开的技能和方法.Pearl Zhu 在 "The Digital Master" 一书中 ...

  3. Csharp中表达式树

    Csharper中的表达式树 这节课来了解一下表示式树是什么? 在C#中,表达式树是一种数据结构,它可以表示一些代码块,如Lambda表达式或查询表达式.表达式树使你能够查看和操作数据,就像你可以查看 ...

  4. KingbaseES Json 系列九:Json路径查询函数

    KingbaseES Json 系列九--Json路径查询函数(JSONB_PATH_EXISTS,JSONB_PATH_MATCH,JSONB_PATH_QUERY,JSONB_PATH_QUERY ...

  5. debian12 linux root能用lightdm登陆xfce桌面,普通用户不能用lightdm登陆xfce桌面,闪一下黑屏和一个_,又回到lightdm

    Fn+Ctrl+F3,进入tty,发现登陆普通用户后再使用startxfce4可以直接进桌面 下面参照 https://forums.opensuse.org/t/normal-user-can-no ...

  6. debian12 linux 安装xfce4

    1.安装显示服务器 sudo apt install xorg 2.安装xfce sudo apt install xfce4 xfce4-goodies 3.安装显示管理器 sudo apt ins ...

  7. linux xfce 设置限制亮度滑块的最小亮度,在屏幕里的xfce设置亮度的最小亮度。

    参照 https://docs.xfce.org/xfce/xfce4-power-manager/preferences 使用代码 xfconf-query -c xfce4-power-manag ...

  8. #双指针#洛谷 7521 [省选联考 2021 B 卷] 取模

    题目传送门 分析 将 \(a\) 排序后从大到小枚举 \(a_k\),注意枚举的时候重复的只考虑一次,那么可以将其它数按照模 \(a_k\) 后排序, 答案只可能来自最大值与次大值之和取模或者之和最接 ...

  9. #线段树,矩阵乘法#LOJ 3264「ROIR 2020 Day 2」海报

    题目 分析 设\(dp[i][0/1/2/3]\)表示以\(i\)结尾1的长度为0/1/2/3的最大值, 那么 \[\begin{cases}dp[i][0]=\max\{dp[i-1][\dots] ...

  10. GitHub互赞快速涨星,最简单的涨星方法

    ​各位代码们,是不是厌倦了在GitHub上孤独地刷着自己的项目页面,眼巴巴地等待那星星数的涨幅?今天给大家安利一个超级实用的新玩意儿--涨星互助平台,一个让你的GitHub项目星星数飞起来的秘密基地! ...