为什么UI框架必须/应该是单线程的?

gmr*_*gmr 12 concurrency erlang swing multithreading locking

以前曾提出过密切相关的问题:

但是这些问题的答案仍然让我在某些方面不清楚.

  • 第一个问题的提问者询问多线程是否有助于提高性能,而回答者大多说它不会,因为GUI不太可能成为现代硬件上2D应用程序的瓶颈.但在我看来,这是一个偷偷摸摸的辩论策略.当然,如果您仔细构建了应用程序,除了UI线程上的UI调用之外什么都不做,那么您将不会遇到瓶颈.但这可能需要大量工作并使您的代码更复杂,如果您有更快的内核或可以从多个线程进行UI调用,那么可能不值得这样做.

  • 一个普遍提倡的架构设计是让视图组件没有回调,除了他们的后代之外不需要锁定任何东西.在这样的架构下,你不能让任何线程使用每个对象的锁来调用视图对象上的方法,而不用担心死锁吗?

  • 我对UI控件的情况不太自信,但只要它们的回调仅由系统调用,为什么它们会导致任何特殊的死锁问题?毕竟,如果回调需要做任何耗费时间,他们将委托给另一个线程,然后我们回到多线程的情况.

  • 如果您可以阻止UI线程,您将获得多线程UI的多少好处?因为异步的各种新兴抽象实际上允许你这样做.

  • 我所看到的几乎所有讨论都假定使用手动锁定来处理并发性,但人们普遍认为手动锁定是在大多数情况下管理并发性的一种不好的方法.当我们考虑专家建议我们使用更多的并发原语(如软件事务内存)或避免共享内存支持消息传递(可能具有同步,如go)时,讨论如何改变?

zxq*_*xq9 16

TL; DR

这是一种强制排序在一个最终将按顺序排列的活动中的简单方法(屏幕按顺序每秒抽取X次).

讨论

处理在系统中具有单一标识的长期资源通常通过用单个线程,进程,"对象"或其他代表关于给定语言的并发性的原子单元来表示它们来完成.回到非限制性,疏忽内核,非时间共享,One True Thread日,这是通过轮询/循环或编写自己的调度系统来手动管理的.在这样的系统中,你仍然在function/object/thingy和singular资源之间有一个1 :: 1的映射(或者你在8年级之前就疯了).

这与处理网络套接字或任何其他长期资源时使用的方法相同.GUI本身只是典型程序管理的许多此类资源之一,通常长期存在的资源是事件排序很重要的地方.

例如,在聊天程序中,您通常不会编写单个线程.你会有一个GUI线程,一个网络线程,也许还有一些其他线程来处理日志记录资源或其他什么.典型系统如此之快以至于更容易将日志记录和输入放入进行GUI更新的同一线程中并不少见,但情况并非总是如此.但是,在所有情况下,每种类别的资源最容易通过授予它们一个线程来推理,这意味着网络的一个线程,GUI的一个线程,以及许多其他线程对于长期操作或要管理的资源而不阻塞其他资源.

为了使生活更轻松,通常不要在这些线程之间直接共享数据.队列比资源锁更容易推理并且可以保证排序.大多数GUI库要么对要处理的事件进行排队(因此可以按顺序对它们进行求值),要么立即提交事件所需的数据更改,但在重新绘制循环的每次传递之前都要锁定GUI的状态.不要紧,之前发生了什么,画的屏幕是世界的状态时,唯一重要的事情那么好吧.这与典型的网络情况略有不同,在这种情况下,所有数据都需要按顺序发送,而忘记其中一些数据不是一种选择.

因此,GUI框架本身不是多线程的,它本身就是一个GUI循环,它需要是一个单独的线程才能合理地管理这个单一的长期资源.编程示例(通常本质上是微不足道的)通常是单线程的,所有程序逻辑都在与GUI循环相同的进程/线程中运行,但这在更复杂的程序中并不常见.

总结一下

由于调度很难,共享数据管理更加困难,并且单个资源只能以串行方式访问,用于表示每个长期持有资源的单个线程和每个长时间运行的过程是构造代码的典型方法.GUI只是典型程序管理的几种资源中的一种.所以"GUI程序"绝不是单线程的,但GUI库通常是.

在简单的程序中,将其他程序逻辑放入GUI线程中没有明显的损失,但是当遇到大量负载或资源管理需要大量阻塞或轮询时,这种方法就会崩溃,这就是为什么你经常会看到事件队列,在几乎任何GUI库的尘土飞扬的角落中提到的信号槽消息抽象或其他多线程/处理方法(这里我包括游戏库 - 而游戏库通常期望你想要基本上构建自己的小部件你自己的UI概念,基本原理非常相似,只是有点低级).

[顺便说一下,我最近做了很多Qt/C++和Wx/Erlang.Qt文档很好地解释了多线程的方法,GUI循环的作用,以及Qt的信号/槽方法适合抽象的方式(因此您不必考虑并发/锁定/排序/调度/ etc非常多).Erlang本质上是并发的,但是wx本身通常是作为管理GUI更新循环的单个OS进程启动的,Erlang将更新事件作为消息发布到它,并且GUI事件作为消息发送到Erlang端 - 从而允许正常的Erlang并发编码,但提供单点GUI事件排序,以便wx可以进行GUI更新循环.]