UI线程和工作者线程
本文转载于:http://blog.csdn.net/libaineu2004/article/details/40398405
1、线程分为UI线程和工作者线程,UI线程有窗口,窗口自建了消息队列,这个UI线程维护“消息队列”,“消息队列”是界面线程和工作者线程的最大区别。所以有用户界面的一般称为UI线程,没有界面的称之为工作者线程,UI线程因为有界面,所以系统会给它维护一个消息队列,工作者线程就没有消息队列。
UI线程又叫界面线程,能够响应操作系统的特定消息,包括界面消息、鼠标键盘消息、自定义消息等,是在普通的worker线程基础上加上消息循环来实现的,在这个消息循环上不断地获取操作系统或其他线程派发过来的消息(就是个线程里面执行for循环的主),再根据这个消息找到对应的消息处理对象/函数,如果暂时没有消息需要处理就让线程等待,让出CPU时间片,以此表现出该线程一直“在线”的状态,直到某一特定退出消息结束线程。
worker线程又叫工作者线程,为了不影响UI线程的长时间任务处理导致的界面操作阻塞,提升用户体验,开以考虑开辟一个worker线程来完成复杂的任务,任务处理完后可以让它立即退出也可以让它继续等待新的任务,看具体需要。一个处理完任务后不退出而处于等待状态的worker线程其实跟UI线程已经没多大区别了,在worker线程也可以自己实现一个任务队列,然后不断地获取任务来达到UI线程的效果。
2、工作者线程原本是没有消息队列,但是你可以强制加一个,一般只要你的线程中出现了GDI的调用就会出现一个消息队列,线程中如果调用了GetMessage(),就可以强制加入了一个消息循环,系统就会给该线程加一个消息队列,同样用PeekMessage()也可以强制系统加入一个消息队列.工作者线程的消息传递是通过PostThreadMessage函数发送的。即:工作线程函数里面如果调用了有关消息的函数,操作系统自动为工作线成创建消息队列。
3、所谓的UI线程有窗口,窗口自建了消息队列。所谓的工作者线程初始状态没有自建消息队列。
4、UI线程消息处理过程
只有在使用MFC框架时才有UI线程和工作者线程之分。UI线程与工作者线程的区别是操作系统为UI线程创建并维护了一个消息队列。
其实线程在创建时(无论是API还是MFC),都是工作者线程。当线程调用发送消息或提取消息或图形用户界面相关的函数时,系统才为其创建一个消息队列和THREADINFO结构,这时的线程才称为UI线程。
VC开发的控制台程序的主线程是工作线程,其他程序的主线程为UI线程。_beginthreadex/CreateThread等函数创建的线程默认为工作线程,AfxBeginThread可以根据参数创建工作者线程和UI线程。
(1) 从用户输入到系统消息队列
操作系统会监视计算机的键盘和鼠标等输入设备,为每一个输入事件生成一个消息,将消息统一临时放到“系统消息队列”内。其中消息的窗口句柄由系统根据鼠标或光标所在区域计算出的。
(2) 从系统消息队列到线程消息队列
系统有专门的线程负责从系统消息队列中取出消息,根据消息的目标对象(窗口句柄),将消息投递到创建它的UI线程对应的消息队列中。每个UI线程有且仅有一个消息队列。
(3 )UI线程处理消息
UI线程启动一个消息循环(Message Loop),每次从本线程所对应的消息队列中取出一条消息,然后根据消息所包容的信息,将其转发给特定的窗体对象,此窗体对象所对应的“窗体过程”函数被调用以处理这些消息。
MSG msg; // 代表一条消息
BOOL bRet;
// 从UI线程消息队列中取出一条消息
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet == -1)
{
// 错误处理代码,通常是直接退出程序
}
else
{
TranslateMessage(&msg); // 转换消息格式
DispatchMessage(&msg); // 分发消息给相应的窗体
}
}
GetMessage()等到有消息才返回,peekmessage()则马上返回消息或空值;GetMessage()获得消息后从队列中删除(WM_PAINT除外),peekmessage()则根据参数PM_NOREMOVE或PM_REMOVE来决定要不要删除。
TranslateMessage()函数主要用于将WM_KEYDOWN和WM_KEYUP消息转换WM_CHAR消息。所以如果要截获WM_KEYDOWN和WM_KEYUP消息,需要重载窗体类的PreTranslateMessage()函数进行分析。
DispatchMessage()函数根据取出的消息中所包含的窗体句柄,将这一消息转发给引此句柄所对应的窗体对象。负责响应消息的函数称为窗体过程(Window Procedure)。如果需要自定义处理,则重载窗体类的DefWindowProc()函数。窗体过程是一个函数,每个窗体一个,形式如下:
LRESULT CALLBACK MainWndProc(……)
{
//……
switch (uMsg) // 依据消息标识符进行分类处理
{
case WM_CREATE:
// 初始化窗体.
return 0;
case WM_PAINT:
// 绘制窗体
return 0;
//
// 处理其他消息
//
default:
// 转去调用系统默认的消息处理函数
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
//……
}
http://blog.csdn.net/hebezai/article/details/10383323
也谈谈我对UI线程和worker线程的理解
不管在什么操作系统,UI线程与worker线程协同工作不可避免地都会面对线程同步执行的问题,这里同步执行不是“对象状态同步”的概念,而是将线程执行“串行化”,比如线程A要执行后续的代码段之前先要等待线程B完成某一特定任务,当然,也可以通过“对象状态同步”的方式来达到这个效果,只是会比较麻烦一点。
以windows系统为例,当worker线程处理完结果后,需要把结果返回给UI线程,好让界面能够更新显示最新的状态,有这么几种方式:
1.直接在worker线程执行处调用API::SendMessage,把消息和附带参数发往UI线程的消息队列,让UI线程从挂起状态恢复成执行态后从消息泵获取处理,如果当前消息泵没有其他消息,则立即处理此消息,否则需等待其他消息处理完后轮到它才执行,而此时worker线程处于等待消息处理的状态,UI线程处理完消息后,它才继续往下执行;
2.直接在worker线程执行处调用API::PostMessage,与SendMessage一样,只不过,这是worker线程不等待处理结果,继续往下走,又叫异步方式。如果PostMessage有附带参数,这时就得谨慎处理了,就像普通的线程异步处理一样,对象必须加锁解锁,除非这个附带参数是临时从堆上分配的对象,由UI线程处理完消息前负责释放,但是这又涉及到模块之间的内存申请、释放问题,搞不好,程序直接core dump。
3.worker线程直接把结果存放于UI线程能访问的某一处,等到UI线程执行到需要用到该结果时才访问,也要对这个对象进行加锁解锁,搞不好容易死锁。
以上3中方法,不管是哪种,处理起来都很麻烦,尽管第一种方式相对比较简单,可是写起代码来还是觉得繁琐,要先定义一个双方认识的消息(还得考虑该消息会不会冲突,是否考虑使用RegisterWindowMessage),定义好消息处理函数,然后添加消息映射宏,而且代码看起来不如顺序执行那么流畅,维护代码时要在这些地方跳来跳去,且还不论有多种同步消息需要同步,时间久了,只会越来越难维护。
接触这种线程“伪同步”一段时间后,自然而然对这种方式反感,开始思考一个问题:为什么操作系统不提供一个线程切换的API供调用,这样多省事啊。等待认真了解线程工作原理后,才发现这想法太天真了。
通常一个线程得以被操作系统调度运行,是因为符合当前优先级配置,且该线程尚有代码需要执行(这样说貌似也不太合理,想不出来),最直观的就是函数调用(不管全局函数也好、构造函数也好。。。),既然是函数调用那肯定有参数、变量入栈出栈,每个线程被挂起后,为了恢复执行后能够之前的状态下继续执行,就必然是一个线程对应一个线程栈,以此线程间互不干扰,各自在自己的一亩三分地上干活。这时再考虑线程“串行化”的问题,如果系统要提供线程切换的API,那么worker线程需要与UI线程同步时就必须让UI线程切换到worker线程拥有的线程栈上来,否则如何访问worker线程中的那些变量、参数,UI线程切换过来后,之前保留的现场又该怎么处理,如果栈是可拷贝的,那UI线程把它拷贝过来后置于栈顶,那栈可用空间不够大怎么办。此外还有种种我这水平未能触及的原因,发现真要这么做的话,这代价实在太高了,还不能保证如预期那样进行。
UI线程和工作者线程的更多相关文章
- Windows线程漫谈界面线程和工作者线程
每个系统都有线程,而线程的最重要的作用就是并行处理,提高软件的并发率.针对界面来说,还能提高界面的响应力. 线程分为界面线程和工作者线程,界面实际就是一个线程画出来的东西,这个线程维护一个“消息队列” ...
- WebWorker:工作者线程初探
WebWorker:工作者线程初探 参考资料: 1.Web Worker 使用教程 - 阮一峰:http://www.ruanyifeng.com/blog/2018/07/web-worker.ht ...
- 高并发的epoll+线程池,线程池专注实现业务
我们知道,服务器并发模型通常可分为单线程和多线程模型,这里的线程通常是指“I/O线程”,即负责I/O操作,协调分配任务的“管理线程”,而实际的请求和任务通常交由所谓“工作者线程”处理.通常多线程模型下 ...
- UI线程与worker线程
也谈谈我对UI线程和worker线程的理解 UI线程又叫界面线程,能够响应操作系统的特定消息,包括界面消息.鼠标键盘消息.自定义消息等,是在普通的worker线程基础上加上消息循环来实现的,在这个消息 ...
- C# 多线程详解 Part.02(UI 线程和子线程的互动、ProgressBar 的异步调用)
我们先来看一段运行时会抛出 InvalidOperationException 异常的代码段: private void btnThreadA_Click(object sender, ...
- Android的UI设计与后台线程交互
本文将讨论Android应用程序的线程模型以及如何使用线程来处理耗时较长的操作,而不是在主线程中执行,保证用户界面(UI)的流畅运行.本文还将阐述一些用户界面(UI)中与线程交互的API.UI用户界面 ...
- 模板应用--UI线程与worker线程同步 模仿c# invoke
由之前的一篇博文 <UI线程与worker线程><UI线程与worker线程>引出,UI线程与worker线程“串行化”在win32上实现是多么没有节操的事情,代码编写麻烦不说 ...
- 为什么说android UI操作不是线程安全的
转载于:http://blog.csdn.net/lvxiangan/article/details/17218409#t2 UI线程及Android的单线程模型原则 使用Worker线程 Commu ...
- MFC工作者线程
//************工作者线程**************1.在头文件中添加UINT ThreadFunc(LPVOID lpParam); 注意应在类的外部 2.添加protected型变量 ...
随机推荐
- (七)git分支的操作
1.git branch——显示分支一览表 2.git checkout -b——创建.切换分支 往feature-A中不断add.commit叫培育分支 git checkout - 切回上一个分支 ...
- NFS Iptables放行服务端口
启动NFS会开启如下端口:1)portmapper 端口:111 udp/tcp:2)nfs/nfs_acl 端口:2049 udp/tcp:3)mountd 端口:"32768--6553 ...
- HTML JavaScript 基础学习
HTML 中肯定会用到 JavaScript 的知识点,会点 JavaScript 的基础知识不会吃亏,其实打算去买JavaScript的教程去专门学习一下,但是交给我的时间不多了,记录一点,能会一点 ...
- 01: requests模块
目录: 1.1 requests模块简介 1.2 使用requests模块发送get请求 1.3 使用requests模块发送post请求 1.4 requests.request()参数介绍 1.1 ...
- 利用.bat(批处理)来删除KEIL编译生成的无用文件
新建一个.txt文件. 在里面输入如下内容: del *.bak /s del *.ddk /s del *.edk /s del *.lst /s del *.lnp /s del *.mpf /s ...
- 洛谷 P2056 采花 - 莫队算法
萧芸斓是 Z国的公主,平时的一大爱好是采花. 今天天气晴朗,阳光明媚,公主清晨便去了皇宫中新建的花园采花.花园足够大,容纳了 n 朵花,花有 c 种颜色(用整数 1-c 表示) ,且花是排成一排的,以 ...
- C++ shared_ptr的用法
一. http://www.cnblogs.com/welkinwalker/archive/2011/10/20/2218804.html 二.http://www.cnblogs.com/Tian ...
- 把一个activity作为弹窗
1.可以在这个activity的xml中设置其高度为某个固定高度 2.在java中:getWindow().setGravity(Gravity.BOTTOM);//设置在底部出现 getWindo ...
- linux下安装/升级openssl
(2810) (1) 安装环境: 操作系统:CentOs7 OpenSSL Version:openssl-1.0.2j.tar.gz 安装: 目前版本最新的SSL地址为 http://www.op ...
- 【基础知识】ActiveMQ基本原理
“来,根据你的了解说下 ActiveMQ 是什么.” “这个简单,ActiveMQ 是一个 MOM,具体来说是一个实现了 JMS 规范的系统间远程通信的消息代理.它……” “等等,先解释下什么是 MO ...