为什么在ProgressChanged事件处理程序中BackgroundWorker不需要Invoke?

4
自从 ProgressChanged 事件处理程序在 DoWork 事件处理程序的某个位置被触发,它们不应该在异步操作线程上调用吗?因为 DoWork 也在该线程上运行,而不是 UI 线程,因此需要使用 Invoke 或 BeginInvoke 来操作控件?
我猜测在 ReportProgress 方法中发生了一些神奇的事情,但它又如何知道在哪个线程上调用 ProgressChanged 事件处理程序才是正确的呢?

1
http://msdn.microsoft.com/en-us/library/system.windows.forms.windowsformssynchronizationcontext 或者你可以使用ilspy来深入了解。 - rene
1个回答

6
当您调用RunWorkerAsync时,BackgroundWorker内部会创建一个与当前同步上下文关联的新AsyncOperation,通过AsyncOperationManager.SynchronizationContext静态属性检索。

此同步上下文将是从SynchronizationContext派生的类的实例。具体类型取决于应用程序使用的同步模型提供程序。如果您正在运行Windows Forms,则为WindowsFormsSynchronizationContext;在WPF上,它将是DispatcherSynchronizationContext

当您随后在后台线程上调用ReportProgress时,BackgroundWorker将在前面提到的SynchronizationContext实例上内部调用Post,从而将操作异步地分派到相关线程。

在Windows Forms中,这是通过Control.BeginInvoke调用实现的;在WPF上,则变成了Dispatcher.BeginInvoke调用。


我刚读了一下rene的评论中提到的WindowsFormsSynchronizationContext。所以为了理解正确:AsyncOperationManager.SynchronizationContext生成“the” WindowsFormsSynchronizationContext,它恰好有默认的AutoInstall设置为true,因此知道……什么?消息队列运行的线程或创建第一个控件的线程?还是只是保存“主窗体”,以便稍后调用BeginInvoke?另外,顺便提一下,当操作的控件在另一个线程上创建时,它不起作用。 - dialer
WindowsFormsSynchronizationContext 存储了对需要调用 BeginInvokeControl (通常是一个 Form)的内部引用。 - Douglas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接