c# - 为什么 'RunWorkerCompleted' 在错误的线程上执行?

标签 c# .net multithreading winforms backgroundworker

在下面的代码中,当 BackgroundWorker 启动时,SynchronizationContext 确实 存在,但是 RunWorkerCompleted 处理程序在与 RunWorkerAsync() 不同的线程上执行,因此会引发异常。为什么?

并且当对 tempForm 的调用被移除时,它运行良好。 (当用 MessageBox 替换 Form 时也是如此。)

(代码显示一个Form,在一秒后启动一个引用另一个Form的BackgroundWorkerf1,然后显示这个第二个表格 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/

相关文章:

c# - 需要 CRC 编程帮助,从 .NET 类到 C 的 CRC32 转换

java - 多线程应用程序中的 ShutDownHook

java - 并发数据结构的想法

c# - 使用 C#/ASP.NET MVC 进行逐帧 MJPEG 流式传输

c# - 在 C# 中使用带有泛型的访问者模式

c# - 运算符 '??' 不能应用于类型 'int' 和 'int' 的操作数

java - 达到较低的 Java 线程限制(503 个线程)

c# - WebAPI 路由模板中的通配符

c# - 组织 WinForm 控件代码

c# - Azure 存储(经典)与 Azure 存储 (V2) 代码不适用于 V2 存储