在下面的代码中,当 BackgroundWorker
启动时,SynchronizationContext
确实 存在,但是 RunWorkerCompleted 处理程序在与 RunWorkerAsync()
不同的线程上执行,因此会引发异常。为什么?
并且当对 tempForm
的调用被移除时,它运行良好。 (当用 MessageBox
替换 Form
时也是如此。)
(代码显示一个Form
,在一秒后启动一个引用另一个Form的BackgroundWorker
f1,然后显示这个第二个表格 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);
}
谁能解释一下这是怎么回事?
最佳答案
问题是因为您从默认同步上下文调用了 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
}
实例化表单似乎将 WindowsFormsSynchronizationContext
与当前线程相关联。有趣的是,在关闭表单后,关联的同步上下文将被设置为默认同步上下文,即使用线程池的同步上下文。
经过一番挖掘后,我发现了 - 乍一看 - 奇怪行为的原因:Control
的构造函数会在必要时初始化 WindowsFormsSynchronizationContext
(请参阅 reference source) .从 ShowDialog
返回后,将不会有任何消息循环,因此 SynchronizationContext.Current
必须重置,在本例中为默认线程池 SynchronizationContext
.
关于c# - 为什么 'RunWorkerCompleted' 在错误的线程上执行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25606397/