SynchronizationContext 有什么作用? [英] What does SynchronizationContext do?
问题描述
在Programming C#一书中,有一些关于SynchronizationContext
的示例代码:
In the book Programming C#, it has some sample code about SynchronizationContext
:
SynchronizationContext originalContext = SynchronizationContext.Current;
ThreadPool.QueueUserWorkItem(delegate {
string text = File.ReadAllText(@"c: emplog.txt");
originalContext.Post(delegate {
myTextBox.Text = text;
}, null);
});
我是线程的初学者,所以请详细回答.首先,我不知道context是什么意思,程序在originalContext
中保存了什么?当 Post
方法被触发时,UI 线程会做什么?
如果我问了一些愚蠢的事情,请纠正我,谢谢!
I'm a beginner in threads, so please answer in detail.
First, I don't know what does context mean, what does the program save in the originalContext
? And when the Post
method is fired, what will the UI thread do?
If I ask some silly things, please correct me, thanks!
例如,如果我在方法中只写myTextBox.Text = text;
,有什么区别?
For example, what if I just write myTextBox.Text = text;
in the method, what's the difference?
推荐答案
SynchronizationContext 有什么作用?
简单地说,SynchronizationContext
表示位置where"代码可能会被执行.传递到其 Send
或 Post
方法 将在该位置被调用.(Post
是 Send
的非阻塞/异步版本.)
Simply put, SynchronizationContext
represents a location "where" code might be executed. Delegates that are passed to its Send
or Post
method will then be invoked in that location. (Post
is the non-blocking / asynchronous version of Send
.)
每个线程都可以有一个与之关联的 SynchronizationContext
实例.通过调用 静态SynchronizationContext.SetSynchronizationContext
方法,可以通过SynchronizationContext.Current
属性.
Every thread can have a SynchronizationContext
instance associated with it. The running thread can be associated with a synchronization context by calling the static SynchronizationContext.SetSynchronizationContext
method, and the current context of the running thread can be queried via the SynchronizationContext.Current
property.
尽管我刚刚写了什么(每个线程都有一个关联的同步上下文),SynchronizationContext
不一定代表一个特定线程;它还可以将传递给它的委托的调用转发到多个线程中的任何一个(例如到 ThreadPool
工作线程),或(至少在理论上)到特定的CPU 内核,甚至到另一个网络主机.您的代表最终运行的位置取决于所使用的 SynchronizationContext
类型.
Despite what I just wrote (each thread having an associated synchronization context), a SynchronizationContext
does not necessarily represent a specific thread; it can also forward invocation of the delegates passed to it to any of several threads (e.g. to a ThreadPool
worker thread), or (at least in theory) to a specific CPU core, or even to another network host. Where your delegates end up running is dependent on the type of SynchronizationContext
used.
Windows 窗体将在创建第一个窗体的线程上安装 WindowsFormsSynchronizationContext
.(这个线程通常被称为UI线程".)这种类型的同步上下文调用正是在那个线程上传递给它的委托.这非常有用,因为与许多其他 UI 框架一样,Windows 窗体只允许在创建控件的同一线程上操作控件.
Windows Forms will install a WindowsFormsSynchronizationContext
on the thread on which the first form is created. (This thread is commonly called "the UI thread".) This type of synchronization context invokes the delegates passed to it on exactly that thread. This is very useful since Windows Forms, like many other UI frameworks, only permits manipulation of controls on the same thread on which they were created.
如果我只在方法中写myTextBox.Text = text;
,有什么区别?
What if I just write
myTextBox.Text = text;
in the method, what's the difference?
您传递给ThreadPool.QueueUserWorkItem
将在线程池工作线程上运行.也就是说,它不会在创建您的 myTextBox
的线程上执行,因此 Windows 窗体迟早会(尤其是在发布版本中)抛出异常,告诉您您可能无法访问 myTextBox
来自另一个线程.
The code that you've passed to ThreadPool.QueueUserWorkItem
will be run on a thread pool worker thread. That is, it will not execute on the thread on which your myTextBox
was created, so Windows Forms will sooner or later (especially in Release builds) throw an exception, telling you that you may not access myTextBox
from across another thread.
这就是为什么你必须以某种方式切换回来";从工作线程到UI线程"(其中创建了 myTextBox
)在该特定分配之前.这是按如下方式完成的:
This is why you have to somehow "switch back" from the worker thread to the "UI thread" (where myTextBox
was created) before that particular assignment. This is done as follows:
当您仍在 UI 线程上时,在那里捕获 Windows 窗体的
SynchronizationContext
,并将对其的引用存储在变量 (originalContext
) 中以备后用用.此时必须查询SynchronizationContext.Current
;如果您在传递给ThreadPool.QueueUserWorkItem
的代码中查询它,您可能会得到与线程池的工作线程相关联的任何同步上下文.一旦您存储了对 Windows 窗体上下文的引用,您就可以随时随地使用它来发送"窗口.代码到 UI 线程.
While you are still on the UI thread, capture Windows Forms'
SynchronizationContext
there, and store a reference to it in a variable (originalContext
) for later use. You must querySynchronizationContext.Current
at this point; if you queried it inside the code passed toThreadPool.QueueUserWorkItem
, you might get whatever synchronization context is associated with the thread pool's worker thread. Once you have stored a reference to Windows Forms' context, you can use it anywhere and at any time to "send" code to the UI thread.
每当您需要操作 UI 元素时(但不再或可能不再在 UI 线程上),请通过 originalContext
访问 Windows 窗体的同步上下文,然后移交将操纵 UI 以Send
或 Post
的代码.
Whenever you need to manipulate a UI element (but are not, or might not be, on the UI thread anymore), access Windows Forms' synchronization context via originalContext
, and hand off the code that will manipulate the UI to either Send
or Post
.
最后的评论和提示:
同步上下文不会为您做什么是告诉您哪些代码必须在特定位置/上下文中运行,哪些代码可以正常执行,而无需将其传递给
同步上下文
.为了做出决定,您必须了解您编程所针对的框架的规则和要求 - 在本例中为 Windows 窗体.
Final remarks and hints:
What synchronization contexts won't do for you is telling you which code must run in a specific location / context, and which code can just be executed normally, without passing it to a
SynchronizationContext
. In order to decide that, you must know the rules and requirements of the framework you're programming against — Windows Forms in this case.因此请记住 Windows 窗体的这条简单规则:不要从创建它们的线程以外的线程访问控件或窗体.如果必须这样做,请使用上述
SynchronizationContext
机制,或Control.BeginInvoke
(这是一种特定于 Windows 窗体的方法,可以执行完全相同的操作).So remember this simple rule for Windows Forms: DO NOT access controls or forms from a thread other than the one that created them. If you must do this, use the
SynchronizationContext
mechanism as described above, orControl.BeginInvoke
(which is a Windows Forms-specific way of doing exactly the same thing).如果您针对 .NET 4.5 或更高版本进行编程,则可以通过转换显式使用
SynchronizationContext
、ThreadPool.QueueUserWorkItem
、control.BeginInvoke
等转到新的async
/await
关键字 和 任务并行库 (TPL),即围绕Task
和 ` c 的 MSDN 参考页面las>Task
类.这些将在很大程度上负责捕获 UI 线程的同步上下文,启动异步操作,然后返回到 UI 线程,以便您可以处理操作的结果.If you're programming against .NET 4.5 or later, you can make your life much easier by converting your code that explicitly uses
SynchronizationContext
,ThreadPool.QueueUserWorkItem
,control.BeginInvoke
, etc. over to the newasync
/await
keywords and the Task Parallel Library (TPL), i.e. the API surrounding theTask
andTask<TResult>
classes. These will, to a very high degree, take care of capturing the UI thread's synchronization context, starting an asynchronous operation, then getting back onto the UI thread so you can process the operation's result.这篇关于SynchronizationContext 有什么作用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!