据我了解,同步上下文的使用是可以让你在UI线程上继续(否则我们将无法更新UI,如果我们尝试,就会出现异常)。
1.正确理解?
我尝试在 .NET Core 2.2 控制台应用程序中执行该示例。使用长时间运行的异步操作创建了一个等待,与等待之前相同的上下文将用于继续。鉴于我知道的上下文存在的原因,我假设线程应该与等待之前相同。但是控制台的输出继续显示,而不是等待之前的线程 ID。
据我了解,由于(.NET Core 控制台)在执行时根本没有同步上下文。确实,那里没有UI,它有什么用。我想它只会像我想的那样在 UWP 和 WPF 等 UI 项目上工作。
但在这里:
我听说在 .NET Core 中不需要应用 ConfigureAwait(false)。是这样吗?
有信息表明 .NET Framework 在非 UI 项目中具有自己的上下文 + 可以在核心项目中创建自定义同步上下文。
2. 为什么在 UI 项目之外创建/使用上下文会有用?
3、为什么.NET Framework默认有同步上下文?
- 我不确定我是否正确理解支持在 .NET Core 后端使用 ConfigureAwait(false) 的论点。我是否正确理解它只有在以下情况下才有意义:(1)我选择了我在背面使用的逻辑的一部分,在一个库中并在 UI 项目中重用它。(2)我害怕第三 -可以带来自己的上下文并以某种方式种植主要项目的派对库。虽然为什么后面需要这样的库也是一个问题。 ?
旧的 ASP.NET 有许多全局可用的对象,主要的一个是
HttpContext.Current. 顺便说一句,ASP.NET Core 有一个用于相同目的的接口IHttpContextAccessor,但它没有被广泛使用:现在很可能编写一个复杂的 Web 应用程序甚至一次都不使用这个接口——而以前它HttpContext.Current总是直接使用或间接执行任何请求时。最初
HttpContext.Current只是一个 ThreadStatic 字段,但是在 ASP.NET 中加入异步后,结果发现 ThreadStatic 字段的值在执行异步操作后“丢失”了。但是如果它IHttpContextAccessor使用一种机制来解决这个问题AsyncLocal,那么 ASP.NET 的创建者就无法做到这一点,因为 ASP.NET 比这种机制更古老。这就是他们提出使用同步上下文的想法的地方。实际上,
SynchronizationContextASP.NET 中的主要使用点是HttpContext.Current异步操作完成后的恢复。同时,同步上下文执行以下任务:
延续的序列化 - 所有异步延续都被排队以避免在访问共享对象时可能出现的竞争 - 在新的 ASP.NET Core 中,此任务被分配给程序员,部分分配给 await 运算符;
考虑异步操作以了解请求处理何时结束 - 在新的 ASP.NET Core 中,使用任务(Task 和 ValueTask)代替。
PS 同步上下文实现链接 - https://referencesource.microsoft.com/#system.web/AspNetSynchronizationContext.cs
同步上下文是一种允许您解决两个相似任务的机制。它允许异步代码在进行异步调用的同一线程上执行,或者如果继续在不同线程上启动,它允许恢复线程绑定的静态值。
第一个案例是关于 UI 的,第二个案例是关于后端的。理论上,您可以创建自己的同步上下文并实现自己的逻辑,但我还没有听说过这种实际的实现。
为什么在 UI 中使用同步上下文?Windows 窗口子系统是在单处理器系统时代设计的,并且在单线程环境中运行良好。它也适用于多线程,但有很大的局限性。您可以
SendMessage从不同的线程调用该函数,但它是同步工作的。如果SendMessage当时从另一个线程执行,您的线程将等待。异步任务旨在提高 CPU 利用率,但这仅在它们完成得非常快时才会发生。等待
SendMessage可能很长,因此 Windows 的创建者只是决定只有来自 UI 线程的任务才能调用它。程序中只有一个UI线程,所以所有这样的调用都是顺序执行的,互不干扰。SendMessage用于更改和获取Windows中所有控件的所有属性的值。这就是上帝的功能。如果我们想改变一些东西,比如标签的文本或输入字段的颜色,我们必须在主线程上做,而 UI 应用程序同步上下文可以帮助我们做到这一点。
为什么要在后端使用同步上下文?相邻的答案已经涵盖了这个话题,但我会以我自己的方式回答,据我所知。
从历史上看,ASP.NET 应用程序只是多线程的。对于每个 Web 请求,服务器都会创建一个线程。来自不同用户的线程不会相互混合,因此可以方便地将请求中的重要数据存储在静态线程变量中。
.NET 中有一个平台支持的概念,并不是每个人都记得,因为无处不在的 DI 正在消亡。
请求中的重要数据是什么?首先,这是代表其提出请求的用户;第二,用户的文化。
如果您需要应用程序安全性,您可以实现身份验证:Basic、Digest、Windows Integrated 或 Bearer。有关当前用户的信息出现在标题中
Authorization。ASP.NET 检索此信息并将其存储在HttpContext.User.多亏了这一点,授权代码变得更简单,您可以显示用户名或制作个人资料页面。
用户的文化是从标题中检索的
Accepted-Languages。如果它出现在那里ru-RU,则该网站了解有必要以俄语提供信息。整个设计在多线程环境中运行良好,但在异步环境中停止运行。您的代码的延续可能会在不同的线程上执行。
这意味着在
await你突然改变用户和他的语言之后。为了避免这种情况,ASP.NET 的创建者为 web 实现了一个同步上下文,该上下文在
HttpContext.Current.然而,这个在每次异步调用上都执行的过程非常耗时。大部分代码不使用文化或当前用户,因此可以在任何空闲线程上运行而无需任何前奏。我们记得,为此我们需要调用
ConfigureAwait(false).只有普通程序员才不会打扰这个调用,所以无论如何同步上下文总是在使用中。
我认为这就是 .NET Core 完全放弃同步上下文的原因。
ConfigureAwait(false)对网络没有意义。该实例
HttpContext可作为控制器的属性使用,因此您无需调用HttpContext.Current.