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 toolkits的开发者就“不负责任”地把GUI设计成了单线程消息队列机制,然后他们还说界面更新一般不是瓶颈,单线程足够了。然后我瞬间想到了3D游戏,单线程对于3D应该是很吃力的,但实际上负责3D绘制的是显卡的GPU,GPU不像CPU那样事无巨细、事必亲躬、鞠躬尽瘁、死而后已,只负责画好它的图就可以了,所以并行起来不是件困难的事。
http://blog.csdn.net/liuqiaoyu080512/article/details/12895005#t1
GUI为什么不设计为多线程(用户事件和底层事件的流程是相反的,每层都加锁效率太低,共用一把锁那就是单线程)的更多相关文章
- 十. 图形界面(GUI)设计3.标签、按钮和按钮事件
标签和按钮也许是图形界面中最常见的两种组件,按钮又总是与激发动作事件有关. 标签 标签(JLabel)是最简单的Swing组件.标签对象的作用是对位于其后的界面组件作说明.可以设置标签的属性,即前景色 ...
- C# 多线程(lock,Monitor,Mutex,同步事件和等待句柄)
本篇从 Monitor,Mutex,ManualResetEvent,AutoResetEvent,WaitHandler 的类关系图开始,希望通过本篇的介绍能对常见的线程同步方法有一个整体的认识,而 ...
- JAVA之旅(三十一)——JAVA的图形化界面,GUI布局,Frame,GUI事件监听机制,Action事件,鼠标事件
JAVA之旅(三十一)--JAVA的图形化界面,GUI布局,Frame,GUI事件监听机制,Action事件,鼠标事件 有段时间没有更新JAVA了,我们今天来说一下JAVA中的图形化界面,也就是GUI ...
- JAVA GUI 事件监听事件 详解 和 案例.
GUI(图形用户界面) Graphical User Interface(图形用户接口) 用图形的 方式, 来显示 计算机操作的 界面, 这样更方便 更直观. CLI Command Line Use ...
- QT虚拟小键盘设计--qt事件循环,事件发送的理解
有人讲到QT5.7及其以后的版本才自带免费的小键盘插件. QT5.10中关于QKeyEvent类:点击打开链接 QT sendEvent和PostEvent, 点击打开链接 my god,我今天安装了 ...
- 中控考勤仪IFace302多线程操作时无法订阅事件
场景: 在各办事点安装中控考勤仪Iface302,各办事点的工作人员上下班报到时使用指纹或面纹进行自动登记,验证成功后将与服务吕进行通讯记录相关的考勤信息. 条件限制: 由于Iface302设备不支持 ...
- 多线程 用户级线程和内核级线程 from C++多核高级编程
转 http://book.51cto.com/art/201006/206946.htm 6.1.1 用户级线程和内核级线程 2010-06-21 20:37 齐宁/董泽惠 译 清华大学出版社 字号 ...
- js监听用户的键盘敲击事件,兼容各大主流浏览器
js监听用户的键盘敲击事件,兼容各大主流浏览器 <script type="text/javascript"> document.onkeydown = functio ...
- C#截取用户的点击事件的代码
在代码过程中中,把做工程过程中常用的代码备份一下,如下代码内容是关于C#截取用户的点击事件的代码,应该是对大家也有好处. private void SomeControl_KeyDown(object ...
随机推荐
- Windows 1252和ISO 8859-1之间的区别(ISO 8859-1就是Latin-1,但1252与Latin1略有不同)
2.6.5. ANSI字符编码和Windows 1252 Windows为了支持英语和西欧字符,自己设计了一个编码,对应的在Code Page号是1252,被称为Windows 1252. Windo ...
- 浅谈程序员创业(要有一个自己的网站,最好的方式还是自己定位一个产品,用心把这个产品做好。或者满足不同需求的用户,要有特色)good
浅谈程序员创业 ——作者:邓学彬.Jiesoft 1.什么是创业? 关于“创业”二字有必要重新学习一下,找了两个相对权威定义: 创业就是创业者对自己拥有的资源或通过努力能够拥有的资源进行优化整合,从而 ...
- mysql use mysql hang
uat-db03:/root# mysql -A -uroot -p1234567 Warning: Using a password on the command line interface ca ...
- 系统监控的工具tsar
近期一直在折腾着elasticsearch,需要对硬件进行评估 大概几方面 内存 cpu 硬盘 网络. iostat vmstat top 几个命令用了一堆,其实需要关注的几个点只要都列出来就可以了 ...
- 【转】各个层次的gcc警告 #pragma GCC diagnostic ignored "-Wunused-parameter" --不错
原文网址:http://blog.csdn.net/lizzywu/article/details/9419145 各个层次的gcc警告从上到下覆盖 变量(代码)级:指定某个变量警告 int a __ ...
- 【转】UltraISO制作U盘启动盘安装Win7/9/10系统攻略
U盘安装好处就是不用使用笨拙的光盘,光盘还容易出现问题,无法读取的问题.U盘体积小,携带方便,随时都可以制作系统启动盘. U盘建议选择8G及以上大小的. 下面一步一步讲解如果制作U盘安装盘: 1.首先 ...
- 修改Fedora 20 启动项
在Fedora 20里面,Fedora 使用了systemd作为系统与服务的管理工具,这个守护进程是系统开机后第一个开启的进程,pid 为1.systemd扮演着初始化系统的角色,主要用于开启与维护系 ...
- Android 开发笔记-Eclipse中文乱码
使用eclipse时经常中文乱码网上搜罗了下解决办法: 使用Eclipse编辑文件经常出现中文乱码或者文件中有中文不能保存的问题,Eclipse提供了灵活的设置文件编码格式的选项,我们可以通过设置 ...
- 【转】linux文件系统之mount流程分析
本质上,Ext3 mount的过程实际上是inode被替代的过程. 例如,/dev/sdb块设备被mount到/mnt/alan目录.命令:mount -t ext3 /dev/sdb /mnt/al ...
- qt model/view 架构自定义模型之QFileSystemModel
# -*- coding: utf-8 -*- # python:2.x #QFileSystemModel """ Qt 内置了两种模型:QStandardItemM ...