最新要闻
- 24层楼高!我国首艘大型邮轮将交付:2500万个零件
- 【天天聚看点】文案没能逃过因AI失业!蓝色光标回应AI取代文案外包:属实
- 全球最资讯丨车主曝极氪001自动加速撞车:官方承认失控、但不担责任
- 《生化危机:死亡岛》角色介绍:克莱尔、瑞贝卡、吉尔太美了
- 播报:腾讯QQ邮箱关联邮箱帐号功能要没了!5月15日终止服务
- 王一鸣:重点激活服务消费,放松中高端商品和服务消费的限制性措施
- 当前热文:《CS:GO》价值百万库存玩家被封:只因给其他玩家留言
- 环球速读:华为智慧屏S3 Pro官宣:顶部配“AI慧眼”、超窄四边
- 比亚迪微型电动车海鸥内饰发布:看齐海豚、卖8万买不
- 环球快讯:豆瓣评分跌至6.4!成龙《龙马精神》票房艰难破亿
- 每日速讯:“小Mate 50 Pro”实锤!华为畅享60X正面揭晓:旗舰同款刘海屏
- 环球今头条!4月15日 厦门邮轮母港公交场站正式启用(附公交调整方案)
- 观点数字化大会 | 德信集团胡一平:一定要积极拥抱数字化改革
- 世界关注:非会员看剧遇3000多秒广告 优酷回应:确实会有 属极个别情况
- 世界今日讯!小米长焦之王!13 Ultra塞进两颗长焦镜头
- 专家称北京打车太便宜应涨价 崔东树曾任汽车销售
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
环球精选![翻译]ExecutionContext vs SynchronizationContext
我最近几次被问到关于 ExecutionContext 和 SynchronizationContext 的各种问题,例如它们之间的区别是什么,“传播”它们意味着什么,以及它们与 C# 和 Visual Basic 中新的 async/await 关键字的关系。我想我会尝试在这里解决其中的一些问题。
(相关资料图)
警告:这篇文章深入到 .NET 的一个高级领域,大多数开发人员永远不需要考虑。
什么是ExecutionContext,使它“传播”是什么含义?
ExecutionContext 是绝大部分开发人员不需要考虑的事情之一。它有点像空气:它存在很重要,但除非在某些关键时刻(例如当它出了问题时),我们才会想到它的存在。实际上,ExecutionContext只是其他上下文的容器。其中一些上下文是辅助性的,而另一些则对.NET的执行模型非常重要,但它们都遵循我在描述 ExecutionContext 时所描述的相同哲学:如果你必须知道它们的存在,要么你正在做超级高级的事情,要么出了问题。
ExecutionContext 与“环境”或“上下文”有关的信息,这意味着它存储与当前运行环境或“上下文”相关的数据。在许多系统中,这种环境信息是在线程本地存储(TLS)中维护的,例如在 ThreadStatic 字段或 ThreadLocal
当你从同步世界转到异步世界时,情况变得更加复杂。突然间,线程局部存储(TLS)变得相当不相关。在同步世界中,如果我执行操作A、B和C,那么这三个操作都在同一线程上执行,因此所有三个操作都受到存储在该线程上的环境数据的影响。但在异步世界中,我可能会在一个线程上启动A,并在另一个线程上完成它,使得操作B可能在不同于A的线程上启动或运行,类似地,C也可能在不同于B的线程上启动或运行。这意味着我们曾经依赖于控制执行细节的这种环境上下文现在不再可行,因为TLS不会“流”穿越这些异步点。线程局部存储是针对线程特定的,而这些异步操作并没有绑定到特定的线程上。但是,通常存在逻辑控制流,我们希望这种环境数据能够随着控制流而传播,使得环境数据从一个线程移动到另一个线程。这就是 ExecutionContext 的作用。
ExecutionContext实际上只是一个状态包,可以用来捕获来自一个线程的所有状态,并在逻辑控制流继续时将其恢复到另一个线程上。可以使用静态的 Capture 方法来捕获 ExecutionContext:
// ambient state captured into ecExecutionContext ec = ExecutionContext.Capture();
并在通过静态运行方法调用委托期间恢复:
ExecutionContext.Run(ec, delegate{ … // code here will see ec’s state as ambient}, null);
.NET框架中所有派生异步工作的方法都以这种方式捕获和恢复 ExecutionContext(除了以“Unsafe”为前缀的方法,这些方法是不安全的,因为它们明确不传播ExecutionContext)。例如,当你使用Task.Run时,Run方法从调用线程中捕获ExecutionContext,将该ExecutionContext实例存储到Task对象中。当以后作为该Task执行的一部分调用Task.Run提供的委托时,将使用存储的上下文通过ExecutionContext.Run进行调用。对于Task.Run、ThreadPool.QueueUserWorkItem、Delegate.BeginInvoke、Stream.BeginRead、DispatcherSynchronizationContext.Post和任何其他你能想到的异步API都是如此。它们都捕获 ExecutionContext,存储它,然后在调用某些代码的执行期间后使用存储的上下文。
当我们谈论“传播ExecutionContext”时,我们正是谈论这个过程,即在一个线程上恢复之前的环境状态,并在稍后的某个时间点将该状态恢复到另一个线程中,而该线程将执行所提供的委托。
什么是SynchronizationContext,以及如何捕获和使用它?
在软件开发中,我们喜欢抽象化。我们很少满足于硬编码到特定的实现上;相反,在编写高级系统时,我们会将特定实现的细节抽象出来,以便稍后可以插入不同的实现,而无需更改高级系统。这就是为什么我们有接口、抽象类、虚方法等等。
SynchronizationContext 只是一个抽象,表示您要在其中执行某些工作的特定环境。以 Windows Forms 应用程序为例,它们有一个UI线程(虽然可能存在多个线程,但在本文中不重要),任何需要使用UI控件的工作都必须在该线程上执行。对于在 ThreadPool 线程上运行代码并需要将工作传回UI以便于对UI控件进行操作的情况,Windows Forms 提供了 Control.BeginInvoke 方法。您将委托传递给 Control 的 BeginInvoke 方法,该委托将在与该控件关联的线程上调用。
因此,如果我正在编写一个组件,需要安排一些工作到ThreadPool,然后继续在UI线程上进行一些工作,我可以编写我的组件来使用 Control.BeginInvoke。但是,现在如果我决定在WPF应用程序中使用我的组件呢?WPF也具有与 Windows Forms 相同的UI线程约束,但它具有不同的机制来传递回UI线程:而不是在与正确线程相关联的控件上使用 Control.BeginInvoke,您可以在与正确线程相关联的 Dispatcher 实例上使用 Dispatcher.BeginInvoke(或InvokeAsync)。
现在我们有两个不同的API可以实现相同的基本操作,那么我如何编写我的组件以使其不依赖于UI框架?通过使用 SynchronizationContext。SynchronizationContext 提供了一个虚拟的Post方法;这个方法简单地接受一个委托并在 SynchronizationContext 实现认为合适的地方、时间和方式下运行它。Windows Forms 提供了 WindowsFormSynchronizationContext 类型,它重写了 Post 方法来调用 Control.BeginInvoke。WPF 提供了 DispatcherSynchronizationContext 类型,它重写了 Post 方法来调用 Dispatcher.BeginInvoke。以此类推。因此,我现在可以编写我的组件使用 SynchronizationContext 而不是将其绑定到特定的框架上。
如果我是为了针对Windows Forms而编写我的组件,我可能会实现类似以下的“去线程池,然后回到UI线程”的逻辑:
public static void DoWork(Control c){ ThreadPool.QueueUserWorkItem(delegate { … // do work on ThreadPool c.BeginInvoke(delegate { … // do work on UI }); });}
如果我改为编写我的组件以使用 SynchronizationContext,我可能会把它写成:
public static void DoWork(SynchronizationContext sc){ ThreadPool.QueueUserWorkItem(delegate { … // do work on ThreadPool sc.Post(delegate { … // do work on UI }, null); });}
当然,需要传递目标上下文才能回到当前环境很麻烦(并且对于某些期望的编程模型是禁止的),因此 SynchronizationContext 提供了 Current 属性,它允许你从当前线程发现能够让你回到当前环境的上下文,如果有的话。这允许你“捕获它”(即从 SynchronizationContext.Current 读取引用并将该引用存储以供以后使用):
public static void DoWork(){ var sc = SynchronizationContext.Current; ThreadPool.QueueUserWorkItem(delegate { … // do work on ThreadPool sc.Post(delegate { … // do work on the original context }, null); });}
传播 ExecutionContext vs 使用SynchronizationContext
现在,我们有一个非常重要的观察结果:传播 ExecutionContext 在语义上与捕获和发布到 SynchronizationContext 非常不同。
当您传播 ExecutionContext 时,您正在捕获来自一个线程的状态,然后将该状态还原,以便在提供的委托执行期间成为环境状态。这与捕获和使用 SynchronizationContext 时发生的情况不同。捕获部分是相同的,因为您正在从当前线程中获取数据,但您然后以不同的方式使用该状态。使用SynchronizationContext.Post 而不是在调用委托期间使该状态成为当前状态,您只是使用捕获的状态来调用委托。委托在哪里、何时以及如何运行完全取决于Post方法的实现。
这如何适用于异步/等待(async/await)?
async 和 await 关键字后面的框架支持会自动与 ExecutionContext 和 SynchronizationContext 进行交互。
每当代码等待一个awaitable,而其awaiter指示其尚未完成(即awaiter的IsCompleted返回false)时,该方法需要暂停,并将通过继续使用awaiter来恢复执行。这是我之前提到的异步点之一,因此ExecutionContext需要从发出await代码的代码流经到继续委托的执行。这由框架自动处理。当异步方法即将暂停时,基础结构会捕获ExecutionContext。传递给awaiter的委托引用此ExecutionContext实例,并在恢复方法时使用它。这就是使ExecutionContext代表的重要“环境”信息在await点之间传播的原因。
框架还支持SynchronizationContext。前面提到的对ExecutionContext的支持已经集成到表示异步方法的“构建器”中(例如System.Runtime.CompilerServices.AsyncTaskMethodBuilder),这些构建器确保在使用任何类型的awaitable时都流经ExecutionContext。相反,支持等待Task和Task
当你使用 await 关键字等待一个任务时,默认情况下,等待器将捕获当前的同步上下文,如果存在的话,当任务完成时,它会将提供的继续委托(continuation delegate) Post 回到该上下文,而不是在任务完成时在任何线程上运行委托,也不是将其安排在线程池上运行。如果开发人员不想要这种调度行为,可以通过更改使用的 awaitable/awaiter 来控制。当您等待 Task 或 Task
请注意,尽管 ConfigureAwait 提供了明确的与 await 相关的编程模型支持,用于更改与同步上下文相关的行为,但是没有与 await 相关的编程模型支持来抑制 ExecutionContext 的传播。这是有意的。ExecutionContext 不是编写异步代码的开发人员应该关心的东西;它是基础设施级别的支持,在异步世界中帮助模拟同步语义(即 TLS)。大多数人可以完全忽略它的存在(并且应该避免使用 ExecutionContext.SuppressFlow 方法,除非他们确实知道自己在做什么)。相反,代码在哪里运行是开发人员应该注意的事情,因此同步上下文上升到需要显式编程模型支持的级别。(实际上,正如我在其他文章中所述,大多数库实现者应该在任务的每个 await 上考虑使用 ConfigureAwait(false)。)
SynchronizationContext 难道不是 ExecutionContext 的一部分吗?
到目前为止,我已经忽略了一些细节,但我不能再继续忽略它们了。
我忽略的主要内容是所有的 ExecutionContext 都能够传播(例如SecurityContext,HostExecutionContext,CallContext等),SynchronizationContext 当然也可以。我个人认为这是API设计中的一个错误,自从很多版本以前.NET引入它以来,这个问题已经引起了一些问题。尽管如此,这是我们现在拥有并且已经拥有很长时间的设计,现在更改它将是一个破坏性的变化。
当调用公共的ExecutionContext.Capture()方法时,它会检查当前的SynchronizationContext,如果存在,则将其存储到返回的ExecutionContext实例中。然后,当使用公共的ExecutionContext.Run方法时,在提供的委托执行期间,捕获的SynchronizationContext将作为当前值进行恢复。
这为什么有问题呢?将SynchronizationContext作为ExecutionContext的一部分传播会改变SynchronizationContext.Current的含义。SynchronizationContext.Current应该是一种您可以访问的方式,以返回到您当前在访问Current时所处的环境,因此,如果SynchronizationContext传播到另一个线程的当前环境,您不能相信SynchronizationContext.Current的含义。在这种情况下,它既可能是返回到当前环境的方式,也可能是返回到在流程中先前发生的某个环境的方式。
作为说明,以下是如何造成问题的示例代码:
private void button1_Click(object sender, EventArgs e){ button1.Text = await Task.Run(async delegate { string data = await DownloadAsync(); return Compute(data); });}
这段代码会发生以下事情。用户单击button1按钮,导致UI框架在UI线程上调用button1_Click方法。代码然后启动一个工作项在线程池上运行(通过Task.Run)。该工作项开始进行一些下载工作并异步等待其完成。随后在线程池上的另一个工作项对该下载的结果进行了一些计算密集型操作,并返回结果,导致在UI线程上等待的Task完成。此时,UI线程处理此button1_Click方法的剩余部分,并将计算结果存储到button1的Text属性中。
如果SynchronizationContext不作为ExecutionContext的一部分传播,我的预期是有效的。然而,如果它传播了,我将非常失望。Task.Run在调用时会捕获ExecutionContext,并使用它来运行传递给它的委托。这意味着,在调用DownloadAsync并等待结果的过程中,UI SynchronizationContext会流到Task中,并在调用时处于当前状态。然后,await将看到当前的SynchronizationContext,并将异步方法的剩余部分作为继续项发送到UI线程上运行。这意味着我的Compute方法很可能在UI线程上运行,而不是在线程池中,导致应用程序响应性问题。
现在的情况有点混乱:ExecutionContext实际上有两个Capture方法,但只有一个是公共的。内部的方法(内部于mscorlib)是大多数从mscorlib公开的异步功能使用的方法,并且它可选地允许调用者在ExecutionContext中捕获SynchronizationContext;相应的,也有一个内部重载的Run方法,支持忽略存储在ExecutionContext中的SynchronizationContext,实际上假装没有捕获到(这是mscorlib中大多数功能使用的重载)。这意味着,在mscorlib中实现核心的任何异步操作都不会将SynchronizationContext作为ExecutionContext的一部分传播,但是在任何其他地方实现核心的异步操作将将SynchronizationContext作为ExecutionContext的一部分传播。我之前提到过异步方法的“生成器”是负责在异步方法中传播ExecutionContext的类型,这些生成器确实存在于mscorlib中,并且它们确实使用内部重载...因此,SynchronizationContext不会随着等待而作为ExecutionContext的一部分传播(这与任务等待程序支持捕获SynchronizationContext并Post回到其中的方式是不同的)。为了帮助处理ExecutionContext传播SynchronizationContext的情况,异步方法基础结构尝试忽略由于传播而设置为当前的SynchronizationContext。
简而言之,SynchronizationContext.Current不会“传播”等待点。
原文链接:https://devblogs.microsoft.com/pfxteam/executioncontext-vs-synchronizationcontext/
关键词:
-
环球精选![翻译]ExecutionContext vs SynchronizationContext
我最近几次被问到关于ExecutionContext和SynchronizationContext的各种问题,例如它们之间的区别是什么...
来源: 全球即时:最全的python正则使用
环球精选![翻译]ExecutionContext vs SynchronizationContext
24层楼高!我国首艘大型邮轮将交付:2500万个零件
【天天聚看点】文案没能逃过因AI失业!蓝色光标回应AI取代文案外包:属实
全球最资讯丨车主曝极氪001自动加速撞车:官方承认失控、但不担责任
《生化危机:死亡岛》角色介绍:克莱尔、瑞贝卡、吉尔太美了
播报:腾讯QQ邮箱关联邮箱帐号功能要没了!5月15日终止服务
王一鸣:重点激活服务消费,放松中高端商品和服务消费的限制性措施
天天最新:新人必看| 移动端“动态化”是什么意思?
guacamole免密登录,guacamole不通过登录界面登录,guacamole指定用户名免密登录
天天快看点丨图数据库驱动的基础设施运维实操
java 如何计算两个汉字的相似度?如何获得一个汉字的相似汉字?
全球讯息:PostgreSQL 备忘清单_开发速查表分享
当前热文:《CS:GO》价值百万库存玩家被封:只因给其他玩家留言
环球速读:华为智慧屏S3 Pro官宣:顶部配“AI慧眼”、超窄四边
比亚迪微型电动车海鸥内饰发布:看齐海豚、卖8万买不
环球快讯:豆瓣评分跌至6.4!成龙《龙马精神》票房艰难破亿
每日速讯:“小Mate 50 Pro”实锤!华为畅享60X正面揭晓:旗舰同款刘海屏
环球今头条!4月15日 厦门邮轮母港公交场站正式启用(附公交调整方案)
环球消息!2023 年十大 API 管理趋势
新资讯:如何打开 plist 文件
每日短讯:Three.js教程:第一个3D场景
【天天时快讯】pymysql操作数据库入门
快看点丨高数上复习
辽宁发行150亿元专项债补充盛京银行资本金
【环球聚看点】《信托大家谈——信托业转型发展对大众意味着什么》
观点数字化大会 | 德信集团胡一平:一定要积极拥抱数字化改革
世界关注:非会员看剧遇3000多秒广告 优酷回应:确实会有 属极个别情况
世界今日讯!小米长焦之王!13 Ultra塞进两颗长焦镜头
专家称北京打车太便宜应涨价 崔东树曾任汽车销售
环球快看:女子疯狂网上购物确诊帕金森:过度伤害性冲动行为
世界最资讯丨自研大模型图片生成被质疑 商汤回应:秒画也有第三方开源模型
关于数智融合,看看这20位专家都聊了什么
热文:在.NET Core使用 HttpClient 的正确方式
看!前端新人如何用ChatGPT开发APP
JMeter-BeanShell预处理程序和BeanShell后置处理程序的应用
当前速读:智能指针基本原理,简单实现,常见问题
今日热门!叶一茜当众骂森碟“心机”,她就这么恨女儿?
【新华500】新华500指数(989001)12日窄幅震荡跌0.10%
沙尘跨过长江一路向南!直击江皖沪多地沙尘现场
要闻:GT Neo5 SE 1TB版只卖2599 真我喊话:现货才是硬道理
RTX 4070今晚发布 结果遭AMD背刺了:显存越大越好、4K至少16GB
全球头条:史上最挤”五一档来了!王一博《长空之王》等17部影片扎堆上映
网传明年中旬上海绿牌蓝牌要合并?官方回应来了
当前速读:梯田为“布” 光影为“沙” 点亮乡村“夜经济”
世界热消息:C#使用Elasticsearch入门
plist文件格式转换器
观速讯丨T-SQL基础教程Day1
天天微头条丨云图说|云数据库GaussDB如何做到卓越性能
环球最资讯丨不怕有味儿 比毛巾划算:云南白药洗脸巾0.06元/张狂促
天天热点评!“回锅沙”要来了!部分地方要注意
全球短讯!果粉最期待的大屏MacBook Air来了!苹果史上第一款
世界视点!首款骁龙8+平板中兴Axon Pad来了:12.1寸超大屏
新动态:4月14日State of Play:将展示超20分钟《最终幻想16》
全球看热讯:电商平台商品详情接口的应用场景
热点!二级指针创建二叉树节点与一级指针创建二叉树节点
身体很诚实!马斯克加入AIGC大战:抢购1万张GPU、挖Deepmind墙角
当前视讯!曾被誉为最靠谱!又一家造车新势力发不出工资了
全球微速讯:火山引擎 DataLeap 推出全链路智能监控报警平台
有病了还能买哪种保险?隐瞒健康告知带病投保会怎么样?
理想汽车全系车型电器真实功率再公开:空调果然是耗电大户!
小米13 Ultra样张首亮相:虚化堪比单反 夜拍效果惊人
《龙之家族》新季开拍
罕见超级激励!东方甄选奖励154员工8.83亿港元
【世界独家】00后网红骑摩托车遇车祸身亡 劝不住:网友感慨年轻 有必要全面禁摩?
易基因:METTL3介导的m6A甲基化谱调控肌肉干细胞成肌细胞状态转换|发育分化
全球快资讯丨中骏集团称已备好即将到期美元债兑付资金 拟发行第二支中债增全额担保中票
3月物价数据透露哪些经济变化?促消费政策获得较大空间
天天报道:iPhone 15 Pro颠覆式设计取消了!郭明錤:苹果因技术问题放弃固态按键
丫丫将通过温控卡车运至机场:即将飞赴上海
又有新品牌了!长安启源A07亮相:颜值超高碾压一众国产
报道:推特突然“死亡”!马斯克正式启动打造美版微信
走过别错过!红蜻蜓126款休闲鞋/运动鞋/皮鞋清仓:79元绝世好价
h3c路由器如何恢复出厂设置?h3c路由器配置命令大全
小米手机卡顿反应慢是什么原因?小米手机卡顿反应慢怎么解决?
ssd极速固态硬盘怎么样?ssd极速固态硬盘排名前十
iphone4的显示屏分辨率是多少?iphone4版本太低怎么下载软件?
AMD速龙QL64CPU性能怎么样?amd速龙系列cpu天梯图
Zabbix“专家坐诊”第187期问答汇总
每日报道:数据开发提效有秘诀!离线开发BatchWorks 六大典型场景拆解
全球观天下!公司入职一个阿里大佬,把 Spring Boot 系统启动时间从 7 分钟降到了 40 秒!
HTML中的pre-load 和 pre-fetch
全球最资讯丨京准GPS北斗卫星网络授时服务器助力高速智慧交通
环球观热点:三月三拜轩辕 | 全球华人同根同源——强月新
世界今亮点!雷军:小米13 Ultra将摆脱手机成片的“塑料味”
头条:淄博老板娘为赶高铁小伙1V1烤串!完美诠释“好客山东”
天天观察:苹果智能戒指专利首曝:VR场景交互只需动下手指
当前速读:1TB仅529元!致态TiPlus 7100固态硬盘新史低 选它还是选三星?
全球视讯!机构一致唱多金价 贵金属板块表现亮眼
世界热议:全网最详细中英文ChatGPT-GPT-4示例文档-场景问题智能生成从0到1快速入门——官网推荐的48种最佳应用场景(附python/node.js/
世界速讯:记一次kvm虚机mysql数据库磁盘扩容操作步骤及其问题小坑
每日视点!第八批国家组织药品集采中选结果公布
天天新资讯:公开叫停ChatGPT的马斯克暗渡陈仓玩得6 网友:他是懂中国兵法的
越野也不费油!坦克300 PHEV实车亮相:动力大增
实测RTX 4090平均20帧!《赛博朋克2077》成新一代“显卡危机”
每公里造价2.1亿!中国最贵高速公路预计2025年建成
全球观热点:被国产车打怕韩系车拼了 起亚新SUV赛图斯预售:仅9.19万元起
【独家】昆明部分店外增设“一米线”:商户可在线内摆摊
环球今日报丨中国国债期货收益指数今日发布
今日精选:比五菱MINI EV还便宜?奇瑞“棒棒糖”亮相:油门到底时速能到100km