.net - 同步上下文的切换是否意味着工作将在拥有同步上下文的线程上运行?

标签 .net multithreading asynchronous task-parallel-library synchronizationcontext

您是否像这样运行任务(而不是 Task ):

public async void button1_click(...)
{
    await Task.Run(...);
}

或在您调用 InvokeRequired 的旧方法上使用检查是否需要在另一个同步上下文上调用当前操作,然后调用 Control.Invoke (例如,在 WinForms 的情况下),如果有同步上下文,则使用捕获的同步上下文执行操作。

然而,这意味着哪两件事?

如果您使用任何方法(无论是旧方法还是新方法)请求在线程池线程上运行任务,这是否意味着:
  • 当线程下线时,同步上下文的切换意味着它会等待拥有同步上下文的线程执行这段代码吗?在 UI 拥有的同步上下文的情况下,是否意味着线程池线程会将操作回传到 UI 线程的消息队列并产生?

  • 或者这是否意味着线程池线程将执行操作,但只会有一个保存同步上下文的引用变量 (System.Threading.ExecutionContext.SynchronizationContext) 的切换,因此,同步上下文只是一个协作规则线程同意遵守?工作还是会由线程池线程来完成?

    当然,通过合作纪律*,我并不是说即使恶意线程决定不在需要的地方切换同步上下文,一切都会正常进行。我的意思是,如果同步上下文引用更改为正确的,最初不拥有同步上下文的线程仍然可以运行。

  • 来自阅读 AsyncMethodBuilder<TResult>.Start 的源代码, System.Threading.Tasks.Task.ExecuteWithThreadLocal System.Threading.ExecutionContext.RunInternal 方法,答案似乎很可能是#2,但我不确定。

    更新

    这也是为什么我认为#2更有可能,但我很想得到纠正。

    如果您只是拿一个 Windows 窗体应用程序并在其上粘贴一个按钮并在 click 事件中运行图片中显示的代码,您可能会看到类似于我的调用堆栈,如图所示。

    enter image description here

    我在调用堆栈中跟踪了每个方法的源代码。我观察到上下文切换发生在 System.Threading.ExecutionContext.RunInternal方法。发生这种情况是因为 System.Threading.Tasks.ExecuteWithThreadLocal方法传递值 true用于调用 System.Threading.ExecutionContext.Run 的最后一个参数方法。请看 line 2823 .

    但是,此后,调用继续进行,没有任何消息发布到 UI 线程的消息队列,并且当它最终到达 System.Threading.Tasks.Task<TResult>.InnerInvoke 时。方法,该方法调用委托(delegate)。

    如果答案是#1,请告诉我消息发布的位置,我会高兴得跳起来,今天会学到一些关于同步上下文的有趣的东西。

    是否发生在 ExecutionContext.SetExecutionContext方法?

    如果答案是#2,如果你能证实这一点,那么,我也会唱一首歌来庆祝我的发现。

    Side Note

    I made this program to test something different, though. I wanted to see where the synchronization context is switched, if it is required, both:

    1. Before the await statement is reached; and
    2. After the await statement, i.e on the continuation callback.

    And my findings have satisfactorily revealed to me the answers to both the questions.

    For anyone curious, the switch is made in the AsyncMethodBuilder's Start method for any code that is before the await expression.

    For code that is after, there is more than one path. One of the paths is depicted in this call stack that is shown in the picture above.

    最佳答案

    我有一个 async intro blog post这解释了 awaitSynchronizationContext.Current 合作.具体来说,await使用捕获的上下文来恢复 async方法。

    所以,这是不正确的:

    the operation is performed using the captured synchronization context, if there is one.



    如果通过“操作”,您的意思是 ...在您的代码中:
    public async void button1_click(...)
    {
      await Task.Run(...);
    }
    

    会发生什么 Task.Run将安排...到线程池线程(Task.Run 总是使用线程池)。 await然后捕获当前 SynchronizationContext (在本例中为 UI 上下文)并返回。当...完成,然后Task.Run返回的任务将完成,button1_click将在该上下文(UI 线程)上恢复。然后它到达方法的末尾并返回。

    ... , SynchronizationContext.Current将是 null .是await设置的任务延续使用其捕获的 SynchronizationContext在 UI 线程上恢复。

    关于.net - 同步上下文的切换是否意味着工作将在拥有同步上下文的线程上运行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37666430/

    相关文章:

    asynchronous - 失败后重新启动MailboxProcessor?

    javascript - 在函数中调用函数时异步 Javascript 上下文中的最佳实践?

    .net - 如何连接到内存中的共享缓存数据库?

    c# - 为什么我的 var 调用不返回 DataListItems

    c# - 将 bool 表达式字符串转换为 .NET 代码

    windows - 互斥体真的更慢吗?

    python - GIL 正在杀死 I/O 绑定(bind)线程

    java - 我应该用 run(){} 包围哪些代码?

    c# - 当其中一项任务失败时,是否有可能从 Task.WhenAll 获得成功的结果?

    .net - 如何将 CancellationToken 与 Tasks.WaitAll() 一起使用