【问题标题】:Passing ExecutionContext (flow) VS capturing it?传递 ExecutionContext(流)VS 捕获它?
【发布时间】:2021-04-03 16:34:21
【问题描述】:

从这里: https://devblogs.microsoft.com/premier-developer/dissecting-the-async-methods-in-c/

Task.Run 或 ThreadPool.QueueUserWorkItem 等方法会自动执行此操作。 Task.Run 方法从调用线程中捕获 ExecutionContext 并将其与 Task 实例一起存储。当与任务关联的 TaskScheduler 运行给定的委托时,它使用存储的上下文通过 ExecutionContext.Run 运行它。

普通ExecutionContext流和捕获有什么区别?对我来说,捕获意味着非常具体的东西——对象状态的快照。就像,闭包在匿名方法/本地方法/等中捕获,它作为字段生成并且具有自己的值,不再反映原始值源(变量)的状态。

所以当我们谈论捕获当前线程的上下文时 - 这与正常的上下文流不同吗?就像,在正常流程中,状态始终是最新的,所以如果父线程上的文化发生变化,孩子会意识到,但是如果EC 被捕获 - 现在子线程只是获取那个时间捕获的快照?我理解正确吗? MSDN 文档在“流”/“捕获”术语的用法上确实不一致。

现在,我们有一个非常重要的观察:流动的 ExecutionContext 在语义上与捕获和发布到 SynchronizationContext 非常不同。

https://devblogs.microsoft.com/pfxteam/executioncontext-vs-synchronizationcontext/

当您流动 ExecutionContext 时,您从一个线程捕获状态,然后恢复该状态,使其在提供的委托执行期间处于环境状态。当您捕获和使用 SynchronizationContext 时,情况并非如此。捕获部分是相同的,因为您正在从当前线程中获取数据,但是您随后会以不同的方式使用该状态。使用 SynchronizationContext.Post 您只需使用捕获的状态来调用委托,而不是在调用委托期间使该状态成为当前状态。 委托运行的地点、时间和方式完全取决于 Post 方法的实现。

这对我来说毫无意义。捕获上下文并在其上发布的想法(在具有 UI 线程的 UI 应用程序的示例中)是在相同的上下文中取回该线程(以更新 UI,duh)。但是如果“委托运行的地点、时间和方式完全取决于 Post 方法的实现。”那么你怎么能确保这一点呢?

【问题讨论】:

    标签: c# .net multithreading


    【解决方案1】:

    这是一个众所周知的复杂主题,Stephen Toub 的文章(虽然非常出色)很难理解。

    ExecutionContext 包含有关您的执行环境的“环境”信息。也就是说,例如SecurityContext(保留在 EC 内部)将被“流动”,这意味着您在线程池线程中将拥有与 UI 线程中相同的安全上下文。它像这样对齐一堆其他细节,以便在同一环境中执行。

    在这种情况下,捕获与流动是持有对上下文的引用与使该上下文成为当前上下文之间的区别。所以当我们在这样的任务上运行一些东西时:

    public static void DoWork()
    {
    
        var sc = SynchronizationContext.Current; <-- SC of UI thread is captured, so a reference has been created to the SC of the UI thread
        ThreadPool.QueueUserWorkItem(delegate
        {
            var scThreadPool = SynchronizationContext.Current; <-- thread pool thread has no SC associated so this is null
            … // do work on ThreadPool
            sc.Post(delegate <-- this is posting to the SC of the UI thread now
            {
                … // do work on the original context
            }, null);
       });
    }
    

    如果您要在委托中捕获SynchronisationContext.Current,您将获得线程池线程的当前同步上下文(在此它将为空,因为它们没有关联)。

    如果同步上下文是流动的,那么我们将在调用时获得 UI 线程 SC 而不是 null

    var scThreadPool = SynchronizationContext.Current;
    

    所以执行上下文流动,这意味着它成为线程池线程的执行上下文,但 SC 没有。最后一个令人困惑的事情是,SC 实际上是执行上下文的一部分,并且可以(但通常不会)流动。在大多数情况下,它不会流动,我们无需担心。

    【讨论】:

    • 我认为您将ExecutionContextSynchronizationContext 混淆了。这是两个完全不同的东西。
    • 它们确实是,我是说它们在某个地方是一样的吗?
    • 您并不是说它们是相同的,但是 OP 询问有关流动 ExecutionContext 的问题,而您的答案大部分是关于捕获 SynchronizationContext。除非我遗漏了什么,否则您的回答似乎与所提出的问题无关。
    • 我以为我在前半部分解决了执行上下文的问题,但我想这可能是因为我写了它:) 我会重新阅读它,看看我是否可以编辑它以使其更清晰.
    猜你喜欢
    • 1970-01-01
    • 2019-01-19
    • 1970-01-01
    • 2013-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-13
    • 2014-09-14
    相关资源
    最近更新 更多