Winform控件和线程

本文关键字:线程 控件 Winform | 更新日期: 2023-09-27 18:14:06

我读到过你不能操作一个在不同线程中的控件。

我有两个使用相同Singleton类的控件。这两个控件在不同的线程中,这就是为什么我使用单例在它们之间进行通信。

我的问题是:它只是Winforms的东西,不能跨线程操作?我能每次都通过单例传递数据吗?还是我必须用其他方式?

编辑:

鉴于我得到的评论,我最好澄清一下,我不是在寻找如何做到这一点,而是在寻找何时需要做这件事,何时不需要做这件事。

Winform控件和线程

Winforms控件只是属于非常大的。net Framework类的非线程安全集合。像List<T>这样基本的东西不是线程安全的。你可以在List的强制转换中做一些事情,你可以使用lock关键字来确保只有一个线程在同一时间访问成员。

但是这对Winforms控件不起作用,您不能在访问控件的操作系统代码中注入lock。因此,您的代码总是运行在创建控件的同一线程上的硬性要求。这正是Application.Run()存在的原因,它是解决生产者-消费者问题的普遍方法。操作系统和其他进程中的多个线程产生,而您的单个UI线程消耗,从而保持UI对象的线程安全。

从技术上讲,可以有多个UI线程,每个线程都有自己的顶层窗口(窗体,而不是子控件)。然而,这也是一个相当危险的场景,SystemEvents。变事件是一个主要的麻烦制造者。工具箱中的许多控件都订阅了该事件,当活动的Windows主题发生变化时,它们使用该事件来重新绘制自己。此事件在单个线程中引发,通常是您创建的第一个UI线程。

这意味着第二个线程上的控件将在错误的线程上获得此事件。这很容易导致死锁。通常,当工作站被锁定(Win+L键)时,切换到安全桌面并返回会触发ThemeChanged事件。调试起来太丑了,看起来像这样,几乎不可能修复。如果你这样做,那么你就不得不创建自己的控件类,而不是使用工具箱。

"不要做"是唯一好的建议,它从来都不是必要的。

如果你有一个线程,你想修改你的控件,你应该调用你的控件的BeginInvoke方法。例如:

Task.Run(() => 
{
    label1.BeginInvoke(new Action(() => 
    {
        label1.Text = "Something";
    }));
})