为什么从另一个线程访问GUI元素是错误的?

时间:2015-09-01 18:10:02

标签: multithreading user-interface

在我使用的每个GUI库中(Swing,Android,Windows Forms,WPF),这个黄金法则规定,不能从另一个线程(GUI线程除外)访问或修改GUI元素。我想这个规则适用于任何GUI库。违反此规则很可能会导致应用程序崩溃。但是,我最近一直在想,为什么会这样呢?我找不到任何深刻的解释。 那么这条规则的低级解释是什么?

4 个答案:

答案 0 :(得分:3)

除非明确设计并构建,否则任何软件都不是线程安全的。

GUI是一个复杂而有状态的野兽,使其线程安全将会非常昂贵。

答案 1 :(得分:1)

这有一个非常简单的原因。通常,UI函数不是线程安全的(因为它们使线程安全会使性能降低)。

答案 2 :(得分:0)

据我所知,这只是不正确:只要正确应用了线程安全技术,Java中的每个对象都可以同时加入。事实是Java Swing对象大部分都没有为多线程做好准备,所以你必须执行外部同步。

有几个实例,你需要多个线程在GUI中进行互操作:游戏,视觉效果,用户事件......

有关GUI和多线程的更多信息: https://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html

答案 3 :(得分:0)

在您列出的内容中,有些可能是现有机制的包装器,因此您必须通过底层GUI框架间接回答这个问题。在多平台GUI框架的情况下,例如Qt,你也将拥有最低的共同点,决定什么是可能的,什么不是。

现在,为什么访问GUI不是线程安全的?在我最熟悉的情况下(win32和X11),访问通常通过发送请求间接执行,有时候等待相应的答案。这通常以原子方式工作,甚至跨越流程边界,因此这不是问题的直接原因。但是,如果从多个线程执行此操作,则可能发生的最坏情况是以不协调的方式修改数据。例如,如果您从两个线程读取,修改和编写相同的小部件,则这些操作可能是交错的,因此实际上只会应用一个线程的修改。

不支持跨线程访问还有其他原因:

  • 在win32中,带有消息的队列是线程本地的,这意味着只有创建窗口的线程才能真正找到并能够处理该窗口的消息。我猜这是一个遗留问题,其中进程是单线程的,而消息队列只是一个全局的。使其成为线程局部的方法与用于使errno线程安全的方法相同。
  • 另一个原因是在表示某个GUI元素的进程内创建了支持对象。例如,MFC(在win32之上)使用从OS'窗口小部件句柄到表示该对象的C ++对象的映射。该映射存储在线程本地存储(跟随线程本地消息队列)中,并且对互斥锁没有保护对C ++对象的访问。从不同的线程访问这些对象是不好的,不是因为它们代表GUI对象,而是因为它们不同步,简单就是这样。
  • 如果您考虑修改窗口小部件树的结构(例如浏览器中的DOM树),您可能非常了解应用程序的其他部分正在执行的操作,或者您需要锁定对整个树的访问权限在每次操作之前为了安全起见。毋庸置疑,这有效地阻止了任何并行操作,因此您还可以执行下一步并且要求所有操作来自一个线程,从而节省整个多线程开销。

那就是说,我相信Qt和C#(可能还有其他人)实际上支持一些跨线程操作。他们将使用一些(或多或少模糊)魔法,将调用转发到GUI线程并再次将结果转发回调用线程。换句话说,他们试图让程序员更方便地进行必要的线程间通信,同时保持单线程GUI的效率和简单性。这不仅限于GUI处理,而是一般的方法,只对GUI特别重要。