是否同步上下文的切换意味着工作将拥有同步上下文中的线程上运行? [英] Does the switching of a synchronization context mean that the work will run on the thread that owns the synchronization context?

查看:200
本文介绍了是否同步上下文的切换意味着工作将拥有同步上下文中的线程上运行?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

不管你运行的任务的(和的不是工作 的),像这样:

 公共异步无效的button1_Click(...)
{
    等待Task.Run(...);
}

或使用上的,你叫老办法 InvokeRequired 来检查,如果有需要调用另一个同步上下文当前操作,然后调用 Control.Invoke (在的WinForms的情况下,例如)时,使用捕获的同步上下文执行的操作,如果有一个

然而,其中两件事情这意味着什么?

如果您请求的任务将使用任何方法线程池线程运行,无论是新的或旧,岂不是:


  1. 在该线程下车地面,同步上下文的切换意味着它将等待拥有同步上下文中执行的那块code的线程?在UI拥有同步上下文的情况下,它意味着线程池中的线程将回发的动作到UI线程的消息队列和屈服?


  2. 还是意味着该线程池线程将执行行动,但会仅仅是一个参考变量( System.Threading.ExecutionContext.SynchronizationContext )保存同步上下文,因此,同步上下文仅仅是一个合作的学科,线程同意遵守?这项工作将仍然由线程池中的线程来完成?

    当然,通过合作社的纪律*,我的意思并不是说一切都会好起来工作,即使恶棍线程决定不转它被要求同步上下文。我的含义是,如果该synchronizataion上下文引用改变为正确的最初不拥有该同步上下文线程仍然可以运行


从阅读源$ C ​​$ C <一个href=\"http://referencesource.microsoft.com/#mscorlib/system/runtime/compilerservices/AsyncMethodBuilder.cs,5916df9e324fc0a1\"相对=nofollow> AsyncMethodBuilder&LT; TResult&GT;。开始 ,的 System.Threading.Tasks.Task.ExecuteWithThreadLocal 和<一个href=\"http://referencesource.microsoft.com/#mscorlib/system/threading/executioncontext.cs,de9ca2ef250c4a00\"相对=nofollow> System.Threading.ExecutionContext.RunInternal 方法,似乎很有可能,答案是#2,但我不知道。

更新

下面也是为什么我认为#2是更容易,但我很乐意被纠正。

如果你只需要一个Windows窗体应用程序,坚持就可以了按钮,运行code,它是在click事件的图片所示,你可能会看到调用堆栈看起来像我的,如图所示相同的画面。

在这里输入的形象描述

我跟着调用堆栈每种方法的源$ C ​​$ C。我观察到,上下文切换在 System.Threading.ExecutionContext.RunInternal 方法发生。这是因为在 System.Threading.Tasks.ExecuteWithThreadLocal 方法传递值真正其通话的最后一个参数在 System.Threading.ExecutionContext.Run 方法。请参阅线2823

不过,此后,呼叫进行,没有任何消息发布到UI线程的消息队列,当它终于到达 System.Threading.Tasks.Task&LT; TResult&GT; .InnerInvoke 方法,该方法调用委托。

如果答案是1,如果你能请告诉我那里的消息发布情况,我会雀跃,并已经了解了同步上下文迷人的东西今天。

是否在 ExecutionContext.SetExecutionContext 方法怎样?

如果答案是#2,如果你能确认这一点,那么,太,我会在我的发现的庆祝唱一首歌。


  

旁注


  
  

我做了这个程序来测试不同的东西,虽然。我想
  看的其中的同步上下文切换,如果是
  要求,既:


  
  

      
  1. 等待语句之前到达;和

  2.   
  3. 等待语句,即在后的延续回调

  4.   

  
  

和我的发现已经圆满地透露给我的答案既
  问题。


  
  

有关的人好奇,开关在 AsyncMethodBuilder 的制作
  开始任何code,它是方法的的的的await 前pression。


  
  

有关code即后,有不止一条路径。一路径的
  在被在上面的图片中显示该调用堆栈所示。



解决方案

我有一个的 异步介绍博客文章解释如何等待工作的SynchronizationContext .Current 。具体来说,的await 使用捕获的上下文的恢复的的异步方法。

所以,这是不正确的:


  

时,使用捕获的同步上下文执行的操作,如果有一个


如果通过操作,你的意思是 ... 在code:

 公共异步无效的button1_Click(...)
{
  等待Task.Run(...);
}

会出现什么情况是, Task.Run 将安排 ... 来一个线程池线程( Task.Run 始终使用线程池)。在等待然后拍摄电流的SynchronizationContext (在这种情况下,UI方面),并返回。当 ... 完成后,再通过任务 Task.Run 返回将完成,而的button1_Click 将恢复这方面(UI线程)。然后它到达的方法和返回的结束

... SynchronizationContext.Current 。这是任务继续通过设置等待用其抓获的SynchronizationContext 来恢复在UI线程上。

Whether you run a task (and not Task) like so:

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

or using on the of old methods where you call InvokeRequired to check if there is a need to invoke the current operation on another synchronization context, and then call the Control.Invoke (in the case of WinForms, for example), the operation is performed using the captured synchronization context, if there is one.

However, which of the two things does this mean?

If you request a task to be run on a thread pool thread using any of the methods, whether old or new, does it mean that:

  1. When the thread gets off the ground, the switching of synchronization context means that it will wait for the thread that owns the synchronization context to execute the piece of code? In the case of UI owned sync context, does it mean that the thread pool thread will post back the action to the message queue of the UI thread and yield?

    OR

  2. Or does it mean that the thread pool thread will execute the action but there will simply be a switch of a reference variable (System.Threading.ExecutionContext.SynchronizationContext) that holds the synchronization context, and as such, the synchronization context is only a co-operative discipline that threads agree to adhere to? The work will still be done by the thread pool thread?

    Of course, by co-operative discipline*, I do not mean that everything will work alright even if miscreant thread decided not to switch the synchronization context where it was required. My implication is that the thread not owning the synchronization context initially can still run if the synchronizataion context reference is changed to the right one.

From reading the source code of AsyncMethodBuilder<TResult>.Start, System.Threading.Tasks.Task.ExecuteWithThreadLocal and System.Threading.ExecutionContext.RunInternal method, it seems highly likely that the answer is #2, but I am not sure.

UPDATE

Here is also why I assume #2 is more likely but I would love to be corrected.

If you just take a Windows Forms application and stick a button on it and run the code that is shown in the picture in the click event, you might see the call stack that looks like mine, as shown in the same picture.

I followed the source code of each method in the call stack. I observed that the context switch happens in the System.Threading.ExecutionContext.RunInternal method. It happens because the System.Threading.Tasks.ExecuteWithThreadLocal method passes the value true for the last parameter of its call to the System.Threading.ExecutionContext.Run method. Please see line 2823.

However, thereafter, the call proceeds without any message posting to the UI thread's message queue and when it finally reaches the System.Threading.Tasks.Task<TResult>.InnerInvoke method, the method invokes the delegate.

If the answer is #1, if you could please show me where the message posting happens, I'll jump with joy and will have learnt something fascinating about the synchronization context today.

Does it happen in the ExecutionContext.SetExecutionContext method?

And if the answer is #2, and if you could confirm that, then, too, I will sing a song in celebration of my discovery.

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.

解决方案

I have an async intro blog post that explains how await works with SynchronizationContext.Current. Specifically, await uses the captured context to resume the async method.

So, this is not correct:

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

If by "the operation", you mean the ... in your code:

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

What would happen is that Task.Run will schedule ... to a thread pool thread (Task.Run always uses the thread pool). The await then captures the current SynchronizationContext (in this case a UI context) and returns. When ... completes, then the task returned by Task.Run will complete, and button1_click will resume on that context (the UI thread). It then reaches the end of the method and returns.

Within the ..., SynchronizationContext.Current will be null. It is the task continuation set up by await that uses its captured SynchronizationContext to resume on the UI thread.

这篇关于是否同步上下文的切换意味着工作将拥有同步上下文中的线程上运行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆