一种常用的GUI多线程编程情况之我见

fish_kun 2010-08-01 08:35:28
在有GUI的多线程编程中,我们常常要求程序实现这样的一种功能。

GUI线程在接到用户某个输入后,生成一个工作线程,这个工作线程可能去查找一堆文件,可能去访问某个外设,可能去执行一段耗时的计算,现在我们期望GUI线程在生成工作线程后,能继续响应用户其它的操作,总之能继续工作。当工作线程做完相应的工作后,GUI线程企图知道工作线程已做完它的工作,GUI线程需要更新相应的界面呈现给用户工作线程的结果。

本人一直企图用教科书式的同步/互斥问题来解决这种工作模式,却发现无论是Windows OS的互斥量,事件,信号量都不能解决这种模式。

其原因很简单,无论对于互斥量,事件还是信号量,当GUI线程与工作线程通过这些对象进行同步/互斥时,它们都会进入一种停止/等待的状态。比如GUI线程创建工作线程,然后等待工作线程的信号量,这时操作系统会把GUI线程挂起,它根本就不能继续执行,这样的效果对于用户体验来说,多线程与单线程是没有分别的,因为工作线程在执行之时,GUI线程一直在等待,它不能相应其它的用户输入。

我本人的解决方法是用传递消息的方法在GUI线程与工作线程之间通信,用消息的方法来实现上面的需求。

GUI在创建工作线程后,并不去等待工作线程的任何信号量,而是继续执行,并自定义一个消息响应函数,当工作线程执行完相应工作后,需要向GUI线程发送这个自定义的消息,通知GUI线程它做完相应工作,GUI线程会在这个消息响应函数中做相应的工作,把工作线程的结果呈现给用户。

由于Windows的消息机制其实是主线程不断地轮询自己消息队列中的消息,然后调用相应的消息处理函数,所以我对上面需求的结论是:只有通过轮询,才能实现真正的异步回调模式,不止是windows,对于linux也是一样。如果在linux下写一个类似的,GUI线程在创建工作线程后并不挂起等待工作线程的状态,而是继续执行,等工作线程状态发生变化后由工作线程通知GUI线程,GUI线程再调用相应的处理函数。我在linux下是用信号来做的,通过GUI线程不停地去轮询自己的信号,来做相应的处理。

总而言之,要实现GUI线程在创建工作线程后并不挂起等待工作线程的状态,而是继续执行,等工作线程状态发生变化后由工作线程通知GUI线程,GUI线程再调用相应的处理函数这样一种功能,我认为轮询是必须的。因为OS不会也不大可能提供一种类似于中断处理的功能来实现线程之间的异步回调模式,想象一下,中断处理中,需要保存寄存器状态之类的操作,在用户设计的程序中,如果提供这样的功能,不单对效率有影响,对系统的破坏也很大,也增加了用户编程的复杂度。

由于本人水平有限,以上认识有可能有很大不足,希望大家能提出不同的见解,多多指教,本人在这谢谢大家,并开分相送!


...全文
401 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
雨中的月儿 2010-08-04
  • 打赏
  • 举报
回复
GetMessage()不是真正意义上的轮询吧,是一种节省CPU的轮询,有消息就处理,没消息就Idle

这样还不够好么?
ColdMooon 2010-08-04
  • 打赏
  • 举报
回复
分享一些经验。
如果需要中止工作线程,有几种办法:
1.TerminateThread,最暴力的办法。缺点在于线程栈不能释放,C++对象不能析构,如果断在一些关键位置,会有意想不到的结果。
2.UI线程置一个标志变量为0,并等待工作线程结束,并关闭句柄。工作线程以一定频率检查标志,是0就退出。
这有个问题:UI线程可能会在等待之前发送消息并结束。这会导致UI线程在收到通知后操作一个无效的句柄。3.我的办法:UI线程置一个标志变量为0,并等待一条工作线程的消息。工作线程以一定频率检查标志,是0就发送消息并退出。
if (running)
{
running = 0;
flag = 0;
MSG msg;
GetMessage(&msg, hWnd, WM_xxx, WM_xxx);
}
写的有些乱。实在不好表达。
MoXiaoRab 2010-08-04
  • 打赏
  • 举报
回复
看了半天,不知道是不是我理解能力不行。
你是希望用中断?还是别的什么?轮询你认为好还是不好?
fish_kun 2010-08-04
  • 打赏
  • 举报
回复
希望大家多多讨论。
teleinfor 2010-08-03
  • 打赏
  • 举报
回复
这种模式,消息通知方式是一个不错的实现的。worker thread处理完成后通知GUI线程收集结果就可以了。这里其实并不涉及所谓的线程同步问题,一般需要严格先后顺序的场景才会使用阻塞式同步控制,就是说线程之间必须等待结果继续。

而这种后台处理结果并不逻辑上影响前台GUI线程的继续,使用通知方式就是一个很好的方法了。线程直接操作界面的方法实不可取,是有问题的,界面的更新都是由自己的一套MESSAGE机制完成的,线程直接操作界面很容易出现消息队列问题,或者资源访问同步问题导致的崩溃无响应等。

随便说说,不对请指正。
UUcall007 2010-08-03
  • 打赏
  • 举报
回复
经验贴 我喜欢
fish_kun 2010-08-02
  • 打赏
  • 举报
回复
windows 在 GetMessage 的时候,如果有消息,则马上返回 1 进入消息处理;如果有 WM_QUIT 消息,则返回 0;如果消息队列无效,返回 -1;如果消息队列有效却没有消息,将把控制权交还给系统,不进行处理。

我说的轮询其实就是这个意思。我以前设想了这样一种异步回调的模式,GUI线程在生成工作线程后继续执行,而不是等待工作线程的返回,当工作线程完成后,通过事先预留的回调函数,让回调函数在GUI线程中执行。我觉得必须要用队列来实现,而不能在工作线程完成后,直接回调GUI线程的函数。事实上的实现时,GUI线程需要不断检查一个队列中,如果队列中有东西,就处理,而工作线程在做完工作后,实际上是通过在这个队列中添加一项,让GUI线程在处理队列时可以得知工作线程处理完成,可以进行相关的操作,而不能通过直接回调的方式实现。
lazy_2010 2010-08-02
  • 打赏
  • 举报
回复
线程结束之后 PostMessage 给窗口很常见啊;这样做没有错。

你说的 Windows 的消息机制是轮询是什么意思?

windows 在 GetMessage 的时候,如果有消息,则马上返回 1 进入消息处理;如果有 WM_QUIT 消息,则返回 0;如果消息队列无效,返回 -1;如果消息队列有效却没有消息,将把控制权交还给系统,不进行处理。
wuchuncai 2010-08-02
  • 打赏
  • 举报
回复
线程中,最好不要修改界面,线程中修改界面,有时会导致出错。而应该向主线程发消息,主线程修改界面。
wuchuncai 2010-08-02
  • 打赏
  • 举报
回复
我也一般都是工作线程开启后,完成工作了,给GUI发个消息,然后GUI去拿结果就行了。
yinyuanqings 2010-08-02
  • 打赏
  • 举报
回复
楼主所说的消息通知的模式很常见啊,相当多的应用程序都是用这种模式设计的...
feilongjilei 2010-08-02
  • 打赏
  • 举报
回复
在处理线程中也可以直接修改界面吧,传个窗口对象指针就行了

15,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧