【问题标题】:Does SynchronizationContext no longer flow with ExecutionContext (going from .NET Framework to .NET Core)?SynchronizationContext 是否不再与 ExecutionContext 一起流动(从 .NET Framework 到 .NET Core)?
【发布时间】:2020-10-19 11:21:43
【问题描述】:

tl;博士

在 .NET Framework 中,SynchronizationContext 是 ExecutionContext 运行的上下文之一。这在 .NET Core 中不再适用了吗?

长问题

在 Stephen Toub 2012 年的博文 ExecutionContext vs SynchronizationContext 中,他写到 SynchronizationContext 如何成为 ExecutionContext 的一部分:

SynchronizationContext 不是 ExecutionContext 的一部分吗?

到目前为止,我已经掩盖了一些细节,但我无法进一步避免它们。

我忽略的主要内容是 ExecutionContext 能够流动的所有上下文(例如 SecurityContext、HostExecutionContext、CallContext 等),SynchronizationContext 实际上就是其中之一。我个人认为,这是 API 设计中的一个错误,自从在 .NET 多个版本之前就已经建立了这个错误,导致了一些问题。尽管如此,这是我们长期以来一直拥有的设计,现在改变它将是一个突破性的改变。

这篇博文继续详细说明了 SynchronizationContext 何时作为 ExecutionContext 的一部分流动,以及何时抑制流动:

故事现在变得有点混乱:ExecutionContext 实际上有两个 Capture 方法,但其中只有一个是公共的。内部的(mscorlib 内部的)是从 mscorlib 公开的大多数异步功能所使用的,它可以选择允许调用者禁止捕获 SynchronizationContext 作为 ExecutionContext 的一部分;与此相对应的是,Run 方法还有一个内部重载,它支持忽略存储在 ExecutionContext 中的 SynchronizationContext,实际上假装没有被捕获(这也是 mscorlib 中大多数功能使用的重载)。这意味着几乎任何其核心实现驻留在 mscorlib 中的异步操作都不会将 SynchronizationContext 作为 ExecutionContext 的一部分流动,但任何其核心实现驻留在其他任何地方的异步操作都会将 SynchronizationContext 作为 ExecutionContext 的一部分流动。

然而,Stephen Toub 在这里清楚地谈到了 .NET Framework,并阅读了一些关于如何在 .NET Core 中实现 ExecutionContext 的源代码,看来这在 .NET Core 中可能已经发生了变化。作为内部 .NET Framework ExecutionContext 方法一部分的布尔 preserveSyncCtx 参数在更现代的 .NET Core 实现中找不到。

Microsoft documentation for ExecutionContext 对于 .NET Framework 和 .NET Core 和状态是相同的

ExecutionContext 类为与执行逻辑线程相关的所有信息提供了一个容器。这包括安全上下文、调用上下文和同步上下文。

无论压缩堆栈流向何处,托管主体、同步、语言环境和用户上下文也流向何处。

这似乎表明 SynchronizationContext 仍应是 ExecutionContext 的一部分。

为了弄清楚是否存在 差异,我编写了以下 NUnit 测试:

using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;

using NUnit.Framework;

[TestFixture]
public class ExecutionContextFlowTests
{
    private class TestSynchronizationContext : SynchronizationContext
    {
        /// <inheritdoc />
        public override SynchronizationContext CreateCopy()
        {
            return new TestSynchronizationContext();
        }

        /// <inheritdoc />
        public override bool Equals(object obj)
        {
            return obj is TestSynchronizationContext;
        }

        /// <inheritdoc />
        public override int GetHashCode()
        {
            return 0;
        }
    }

    [Test]
    public async Task Test()
    {
        /* Arrange */

        var syncCtx = new TestSynchronizationContext();

        Task<ExecutionContext> t;
        using (ExecutionContext.SuppressFlow())
        {
            t = Task.Run(() =>
            {
                SynchronizationContext prevCtx = SynchronizationContext.Current;
                SynchronizationContext.SetSynchronizationContext(syncCtx);
                try
                {
                    return ExecutionContext.Capture();
                }
                finally
                {
                    SynchronizationContext.SetSynchronizationContext(prevCtx);
                }
            });
        }

        ExecutionContext capturedContext = await t.ConfigureAwait(false);
        Assert.That(capturedContext, Is.Not.Null);
        Assert.That(SynchronizationContext.Current, Is.Not.EqualTo(syncCtx));

        /* Act */

        var syncCtxBox = new StrongBox<SynchronizationContext>();
        ExecutionContext.Run(
            capturedContext,
            box => ((StrongBox<SynchronizationContext>)box).Value = SynchronizationContext.Current,
            syncCtxBox
        );

        Assert.That(syncCtxBox.Value, Is.EqualTo(syncCtx));
    }
}

然后,瞧,断言 passes if run with .NET Framework,但 fails on .NET Core(我使用了 .NET Framework 4.2.7 和 .NET Core 3.1)。

所以我的问题是:博客文章和 Microsoft 文档是否已经过时,Stephen Toub 所说的“API 设计错误”已在 .NET Core 中“修复”,还是我遗漏了什么?

【问题讨论】:

  • @Dai 不,它没有。在 .NET Framework 和 .NET Core 上,该测试的“外部范围”中 SynchronizationContext.Current 为 null,我的测试使用自定义同步上下文来断言它是否与 ExecutionContext 一起运行。

标签: c# .net .net-core


【解决方案1】:

看起来在 .NET Core 中,至少在当前版本 3.1 中,ExecutionContext 不再捕获 SynchronizationContext。看到它here in the ExecutionContext source code。不过,如果同步上下文在 ExecutionContext.Run 回调中发生了变化,它将被恢复。

我认为这是有道理的,因为使用 .NET Core,SynchronizationContext 仅在前端代码中真正相关。他们的目标是尽可能优化服务器端代码,所以他们删除了那部分。

【讨论】:

    【解决方案2】:

    Microsoft 已经承认 ExecutionContext 的行为确实随着 .NET Core 发生了变化,并在 update to their official documentation 中对此进行了澄清。作为更改的一部分,他们添加了以下句子:

    同样在 .NET Core 中,同步上下文不与执行上下文一起流动,而在 .NET Framework 中,在某些情况下可能。

    【讨论】:

      猜你喜欢
      • 2019-12-07
      • 2022-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-10
      • 2021-09-24
      • 1970-01-01
      • 2023-03-03
      相关资源
      最近更新 更多