同一窗口上有多个UI线程

时间:2009-10-30 23:37:03

标签: multithreading user-interface thread-safety

我不想要多个窗口,每个窗口都有自己的UI线程,也不需要在单个UI线程上引发事件,而不是后台工作者和通知,也不需要那些Invoke,BeginInvoke。

  • 我对一个允许多个线程以安全方式更新同一窗口的平台感兴趣。像第一个线程那样创建了三个按钮,第二个线程创建了另外五个按钮,它们都可以访问它们,更改它们的属性并删除它们而不会产生任何不必要的后果。
  • 我希望在没有调用的情况下对UI进行安全的多线程访问,这是一个可以直接从任何线程访问UI对象而不会引发错误的平台,例如“只能从创建它的线程访问该对象”。如果必须的话,让我进行同步,不要阻止我以直接的方式交叉访问UI。

8 个答案:

答案 0 :(得分:16)

我要投票但是...... Go Go Gadget Soapbox。

在一般情况下,无法使用多线程GUI。它一次又一次地被尝试,它永远不会出现。所有主要窗口框架都遵循单线程ui模型并非巧合。他们没有互相抄袭,只是问题的限制导致他们得到同样的答案。很多人比你更聪明,或者我试图解决这个问题。

可能可以为特定项目实现多线程ui。我只是说在一般情况下不能这样做。这意味着你不太可能找到一个可以做你想做的框架。

问题的关键是这个。将gui组件设想为链(实际上它更像是树,但链很容易描述)。按钮连接到框架,连接到框,连接到窗口。系统/操作系统和用户有两个事件来源。系统/ OS事件起源于链的底部(窗口系统),用户事件起源于链的顶部(按钮)。这两个事件都必须穿过gui链。如果两个线程同时推送这些事件,则它们必须受互斥保护。但是,没有已知的算法可以在两个方向上同时遍历双链表。它很容易死锁。 GUI专家尝试并试图找出解决死锁问题的方法,最终得出了我们今天使用的称为模型/视图/控制器的解决方案,也就是一个线程运行UI。

答案 1 :(得分:7)

您可以创建代理的线程安全的Producer / Consumer队列。 任何想要更新UI组件的线程都会创建一个封装要执行的操作的委托,并将其添加到qeueue中。 UI线程(假设所有组件都在同一个线程上创建)然后定期从队列中提取项目,并执行委托。

答案 2 :(得分:3)

我不相信这样的平台本身

没有什么可以阻止你说.Net并创建所有新的控件,这些控件是线程安全的并且可以像那样工作(或者可能只是你需要的部分),这不应该是一个非常大的工作(虽然绝对是不小的工作)因为你可以从基本控件派生并覆盖任何线程不安全的方法或属性。

真正的问题是为什么?由于所有锁定,它肯定会更慢。说你的一个线程正在用UI做一些事情,它必须锁定它正在工作的窗口,否则它可以在不知道其他线程的情况下被更改。因此,通过所有锁定,您将花费大部分的绘图时间,并等待线程上的锁和(昂贵的)上下文切换。你也许可以把它变成异步,但这似乎并不安全(也可能不是),因为你认为刚创建的控件可能存在也可能不存在,并且就像是

Panel p=new Panel();
Button b=new Button();
WaitForControlsCreated(); //waits until the current control queue is cleared
p.Controls.Add(b);

这可能同样慢......

所以真正的问题是为什么?这样做的唯一“好”方法就是将invoke抽象出去,这样就可以从非UI线程中添加控件了。

我认为你误解了线程是如何工作的以及实际使对象线程安全所需要的

答案 3 :(得分:2)

接受任何更新GUI的代码都必须在GUI线程上。 学习使用BeginInvoke()。

答案 4 :(得分:1)

在Windows上,Window handles have thread affinity。这是Window管理器的限制。让多个线程访问Windows上的同一窗口是个坏主意。

答案 5 :(得分:1)

我很惊讶地看到这些答案。

只有像C#这样的高级语言框架对GUI元素有线程限制。

在SDK层,Windows是100%应用程序控制的,除了非常重要的细节之外,对线程没有任何限制。例如,如果多个线程想要写入窗口,则需要锁定互斥锁,获取设备上下文,绘制,然后释放上下文,然后解锁互斥锁。获取和释放设备上下文以进行绘制需要在同一个线程上......但这些通常在10行代码之内。

甚至没有专门的线程可以关闭Windows消息,无论线程调用“DispatchMessage()”是WINPROC将被调用的线程。

另一个小的线程限制是你只能“PeekMessage”或“GetMessage”一个在当前线程上创建的窗口。但实际上这是非常小的,无论如何你需要多少消息泵。

绘图完全与Windows中的线程断开连接,只需将您的DC互斥用于绘图。您可以随时随地绘制,而不仅仅是在WM_PAINT消息上绘图。

答案 6 :(得分:0)

答案 7 :(得分:-1)

基于我对您的需求的猜测,您需要一个Windows窗体并且有异步执行某些例程的方法(比如多线程),是吗?

通常(对于.NET WinForms的情况)Control.Invoke / Control.BeginInvoke用于某种效果,我认为你想要的。

这是一篇可能有用的有趣文章:http://www.yoda.arachsys.com/csharp/threads/winforms.shtml