2016-10-20 35 views
1

我读过,你不能操纵在不同线程中的控件。Winform控件和线程

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

我的问题是: 它只是Winforms的东西,不能跨线程操纵?我可以每次都通过Singleton传递数据而不会失败,还是我必须以其他方式执行此操作?

编辑:

由于我得到的意见,我会更好地澄清,我不是在寻找如何做到这一点,但是当我需要做的是,当我不知道。

+5

“这两个控件在不同的线程中”...让我们从这里开始。你不应该在单独的线程上有控制。 UI组件应该只存在于一个线程上:主UI线程。如果你想从一个单独的线程操作主线程上的控件,可以通过控件的Invoke方法来实现。这将安全地安排在事件循环接近时在控件上执行的操作。 –

+0

我不能控制这些控件是如何实现的,它们是CRM框架的独立插件,这就是它们如何完成的。我知道调用的东西,我的问题更多的是当我需要使用它,当我不在这种情况下。 – Graham

+0

@Graham你不需要控制它们是如何实现的。在一个UI线程上创建它们,并且只能访问它们或从UI线程操纵它们。 – Servy

回答

5

Winforms控件只属于非线程安全的非常大的一组.NET Framework类。像List<T>这样基本的东西不是线程安全的。在List的转换中,您可以使用关键字lock来确保只有一个线程同时访问成员。

但是,这不能用于Winforms控件,您不能在也访问该控件的操作系统代码中注入lock。因此,您的代码总是在创建控件的同一个线程上运行,这非常困难。这是Application.Run()存在的原因,它是producer-consumer problem的通用解决方案。在操作系统和其他进程中产生多个线程并且您的单个UI线程消耗,从而保持UI对象线程安全。

在技术上可能有多个UI线程,每个线程都有自己的顶级窗口(窗体,而不是子控件)。然而,这也是一个相当危险的情况,SystemEvents.ThemeChanged事件是一个重大的麻烦制造者。工具箱中的许多控件都订阅该事件,当活动的Windows主题发生更改时,他们使用它来重绘自己。此事件在单个线程上引发,通常是您创建的第一个UI线程。

这意味着第二个线程上的控件将在错误的线程上获取此事件。这很容易造成僵局。当工作站被锁定时(Win + L键),通常切换到安全桌面并返回触发ThemeChanged事件。调试过于丑陋,看起来like this,几乎无法修复。如果你这样做,那么你几乎不得不创建自己的控件类而不使用工具箱。

“不要这样做”是唯一的好建议,它从来没有必要。

0

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

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

如果你可以使用'Task.Run',你也可以使用'async/await'和* not *必须使用委托。使用'IProgress '接口也可以更好地报告跨线程的进程,而不需要将硬编码控制转换为后台代码 –