isp*_*iro 3 .net c# multithreading backgroundworker winforms
在下面的代码中,当BackgroundWorker启动时,SynchronizationContext 确实存在,但仍然,RunWorkerCompleted处理程序在不同的线程上执行RunWorkerAsync(),因此抛出异常.为什么?
当呼叫tempForm被删除时,它运行正常.(和同代以当MessageBox了Form那里.)
(代码显示a Form,启动一秒后BackgroundWorker引用另一个Form f1,然后显示第二个Form f1.)
public static Form1 f1;
static BackgroundWorker worker = new BackgroundWorker();
[STAThread]
static void Main()
{
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
f1 = new Form1();
using (Form1 tempForm = new Form1()) tempForm.ShowDialog();
//MessageBox.Show("A MessageBox won't cause the exception later. Only the Form does.");
if (SynchronizationContext.Current == null) throw new Exception("This is NOT thrown");
worker.RunWorkerAsync();
Application.Run(f1);
}
static void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show(f1, "Inside RunWorkerCompleted");
//Throws: Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on.
}
static void worker_DoWork(object sender, DoWorkEventArgs e)
{
Thread.Sleep(1000);
}
Run Code Online (Sandbox Code Playgroud)
谁能请解释这里发生了什么?
问题是因为您RunWorkerAsync从默认同步上下文中调用.举个例子:
public static void Main()
{
var ctx1 = SynchronizationContext.Current; // returns null
var form = new Form();
var ctx2 = SynchronizationContext.Current; // returns a WindowsFormsSyncContext
form.ShowDialog();
var ctx3 = SynchronizationContext.Current; // returns a SynchronizationContext
worker.RunWorkerAsync(); // wrong context now
}
Run Code Online (Sandbox Code Playgroud)
实例化表单似乎将a WindowsFormsSynchronizationContext与当前线程相关联.有趣的是,在关闭表单后,关联的同步上下文将被设置为默认值,即使用线程池的同步上下文.
经过一番挖掘后,我发现了 - 乍一看 - 奇怪行为的原因:构造函数Control初始化WindowsFormsSynchronizationContext必要时(参见参考源).一旦从ShowDialog那里返回,就不会有任何消息循环,因此SynchronizationContext.Current必须重置,在这种情况下是默认的线程池SynchronizationContext.