首页 > 其他分享 >(转)GUI为什么不设计为多线程

(转)GUI为什么不设计为多线程

时间:2023-07-21 17:04:25浏览次数:43  
标签:单线程 GUI 队列 线程 消息 设计 多线程


在我们这批新人转正评审的时候,我师父问了我的小伙伴一个问题:为什么一些更新界面的方法只能在主线程中调用?师父没有问我这个问题,让知其然但不知其所以然的我有种侥幸逃过一难的心情。我想如果回答那是因为Android GUI库是单线程消息机制的,更新界面的操作必须放到主线程中执行,那师父可能继续问为什么Android GUI要设计成单线程的,我就不知道了。

为什么它非得设计为单线程的?多线程不是更好吗?带着点好奇感和求知欲以及鄙视权威的无畏精神我在google中展开了搜索,并最终找到了一个令我满意的解释,欣喜之余将我的理解分析给大家。

单线程消息队列机制

首先我还是说一下我对GUI单线程消息队列机制的理解,这是我大学里几年编程经验赚来的知其然的部分。

Android、Swing、MFC等的GUI库都使用单线程消息队列机制来处理绘制界面、事件响应等消息,在这种设计中,每个待处理的任务都被封装成一个消息添加到消息队列中。消息队列是线程安全的(消息队列自己通过加锁等机制保证消息不会在多线程竞争中丢失),任何线程都可以添加消息到这个队列中,但是只有主线程(UI线程)从中取出消息,并执行消息的响应函数,这就保证了只有主线程才去执行这些操作。

单线程消息队列机制存在一个问题:消息响应函数中不能有耗时长的、计算密集型的操作,因为主线程在努力地处理这样的操作的时候就无法去处理其它的积压在消息队列中的绘制消息、事件消息了(一个消息处理完了主线程才会去队列中取下一个消息),这时候就会出现按键无响应、点击无反应的情况。

但这个问题有完美的解决方案,我们可以在消息响应函数中启动另一个工作线程(Worker Thread)来执行耗时操作,这样在线程启动起来后这个消息就算处理完了,主线程可以取下一个消息了,这时候主线程和还未执行完计算任务的工作线程就在操作系统的调度下并驾齐驱地狂奔了(调度算法会保证两个线程并发或并行地执行,不会专宠某个线程)。

一般我们在耗时任务执行完后还要更新界面展示计算的结果,由于我们不能直接在工作线程中更新界面,所以可能有些小伙伴直接在消息响应函数中线程start后就接着调用join来等待线程结束以更新界面,这其实相当于把耗时任务直接放在主线程去执行,因为在消息响应函数中join其实就是主线程在join,积压的消息是得不到处理的。正确的处理办法是将耗时任务改为异步通知机制,即工作线程向消息队列中添加消息以通知主线程耗时任务完成了,这样主线程在启动工作线程后就不需要主动地去调查任务的进展了,“任务结束的时候它会通知我的”,主线程如是说。

工作线程向主线程的消息队列添加消息的常用方法如下:

l Android:Acitvity.runOnUiThead、Handler.post、AsyncTask

l Swing:SwingUtilities.invokeLater

l Win32、MFC:自定义用户消息,在工作线程中PostMessage

GUI为什么不设计为多线程

大部分的GUI toolkits都是设计为上面的单线程消息队列机制,为什么不设计为多线程的呢?如果GUI是多线程的,CPU又是多核的话,多个CPU核心可以并行地执行绘制等操作,界面响应的速度应该是成倍提升的;而且就算是其中有多线程共享的资源加锁不就行了吗?

在google搜索的过程中我看到了负责Swing开发的一个大师的一篇博客《Multithreaded toolkits: A failed dream?》:

https://weblogs.java.net/blog/kgh/archive/2004/10/multithreaded_t.html

从中我了解到开发多线程的GUI toolkits是一件吃力不讨好的事,不仅开发难度大Bug多多,用起来也未必可以获得理想中的效果,其中的死锁和竞争,大师们也感到头疼。

多线程GUI加锁困难

为什么这么困难?大师讲了一个例子,我们通过用户级的代码去改变界面如TextView.setText走的是个自顶向下的流程:

(转)GUI为什么不设计为多线程_移动开发

而系统底层发起的如键盘事件、点击事件走的是个自底向上的流程:

 

(转)GUI为什么不设计为多线程_操作系统_02

这样就麻烦了,因为为了避免死锁,每个流程都要走一样的加锁顺序,而GUI中的这两个流程却是完全相反的,如果每一层都有一个锁的话加锁就是个难以完成的任务了,而如果每一层都共用一个锁的话,那就跟单线程没区别了。

于是GUI toolkits的开发者就“不负责任”地把GUI设计成了单线程消息队列机制,然后他们还说界面更新一般不是瓶颈,单线程足够了。然后我瞬间想到了3D游戏,单线程对于3D应该是很吃力的,但实际上负责3D绘制的是显卡的GPU,GPU不像CPU那样事无巨细、事必亲躬、鞠躬尽瘁、死而后已,只负责画好它的图就可以了,所以并行起来不是件困难的事。

标签:单线程,GUI,队列,线程,消息,设计,多线程
From: https://blog.51cto.com/u_16200746/6803341

相关文章

  • Spring框架的设计理念与设计模式(4)-Context组件
    关键词:Context,组件,Spring,BeanFactory,容器,Ioc上一章:[b]Spring框架的设计理念与设计模式(3)-Bean组件[/b][url]http://javapub.iteye.com/blog/751550[/url]下一章:[b]Spring框架的设计理念与设计模式(5)-Core组件[/b][url]http://javapub.iteye.com/......
  • iOS 多线程自己的理解
    .创建线程的平均开销:内存堆栈:主线程——1M,子线程——512K时间:基本可以忽略不计a.不可改变的对象,通常是线程安全的b.主线程负责处理响应事件线程安全的类和函数:NSArray,NSData,NSNumber.....非线程安全:NSBundle,NSCoder,NSArchiver......
  • Python3 pyautogui安装成功,但是不能引用
    安装使用的是pycharm,python3.8python-mpipinstallpyautogui-ihttps://pypi.tuna.tsinghua.edu.cn/simple,显示Successfully。 引用importpyautogui报错:Nomodulenamed'pyautogui'解决办法尝试各种网上的方法,查询环境等,不太懂,感觉没有问题偶然发现虽然piplist有......
  • 接口默认方法,接口应用和适配器设计模式
    静态方法只能通过接口名调用!!! 私有方法主要用于提取两个不同方法的相同部分,两个不同方法分别调用私有方法,从而简化代码 ......
  • python导航栏的设计
    Python导航栏的设计简介导航栏是一个常见的网页组件,用于在网页中提供导航链接,使用户能够方便地浏览和访问网站的不同页面。在本文中,我将向您介绍如何使用Python实现一个简单的导航栏。整体流程下面是实现导航栏的整体流程:步骤描述1.创建一个包含导航链接的HTML模板......
  • 2017年全国大学生电子设计竞赛F题方案及制作过程分享
    8月9日,2017年全国大学生电子设计竞赛在全国31个赛区同时开幕。本届比赛有来自全国千余所院校、共4万余名学生报名参加,命题涵盖了从基础到综合应用等多个领域。下面即将分享的是论坛资深网友@RF-刘海石对于本科组F题:调幅信号处理实验电路的制作过程及方案经过2天的时间,我已经尽......
  • 浅谈ADC驱动器设计
    引言谈及ADC设计时,必然要了解输入信号是什么、具有什么样的特性及采集系统的指标是什么,所以ADC的输入设计也是采集系统设计的一个难点和重点,需要根据不同的要求设计ADC的前端电路。1.高速ADC模拟输入指标对于高速ADC系统而言,我们必须明确其输入的指标要求,才能更好的发挥出ADC......
  • Unity UGUI的VerticalLayoutGroup(垂直布局)组件的介绍及使用
    UnityUGUI的VerticalLayoutGroup(垂直布局)组件的介绍及使用1.什么是VerticalLayoutGroup组件?VerticalLayoutGroup是UnityUGUI中的一种布局组件,用于在垂直方向上自动排列子对象。它可以根据子对象的大小和布局设置,自动调整子对象的位置和大小,实现垂直布局效果。2.VerticalLay......
  • Unity UGUI的VerticalLayoutGroup(垂直布局)组件的介绍及使用
    UnityUGUI的VerticalLayoutGroup(垂直布局)组件的介绍及使用1.什么是VerticalLayoutGroup组件?VerticalLayoutGroup是UnityUGUI中的一种布局组件,用于在垂直方向上自动排列子对象。它可以根据子对象的大小和布局设置,自动调整子对象的位置和大小,实现垂直布局效果。2.VerticalLayo......
  • 数据结构练习笔记——链式栈的设计与实现
    链式栈的设计与实现【问题描述】采用链式存储结构实现栈的基本操作,并借助栈实现进制转换。【输入形式】整数【输出形式】二进制数【样例输入】10【样例输出】1010#include<iostream>usingnamespacestd;#include<stdlib.h>structsnode{intdata;sn......