关于消息循环

来源:互联网 发布:淘宝店铺收藏 编辑:程序博客网 时间:2024/06/05 20:08

最近尝试在多线程里使用消息队列代替事件和临界区

原因很简单,内核对象太多维护复杂,虽然处理消息队列的逻辑也很复杂,但相对维护一堆内核对象句柄要容易得多

而且,不影响执行效率(消息队列自动销毁,创建只要调用一次相关的API即可,比如user32.dll的函数)


然而奇怪的事情出现了,在几个线程空闲时CPU占用率很高

代码诸如此类:

while(True){// 获取线程消息和窗口消息dwRet = PeekMessageA(&stMsg, NULL, 0, 0, PM_REMOVE);// PM_NOYIELDif(dwRet == FALSE){Sleep(0);continue;// 没有消息, 放弃当前线程时间片, 继续循环}if(stMsg.message == WM_QUIT){break;// 退出消息}...

True是我自定义的布尔枚举,值为1

Sleep(0);是主动放弃当前线程时间片,这个在之前XP下屡试不爽,经常以此代替GetMessage()

因为GetMessage的文档太罗嗦,比如WM_QUIT返回值不同,错误返回-1

我试着创建两个空闲线程,结果CPU占了50%,正好使用了两个核心(我的cpu是FX-4100推土机,4核3.6GHz)

也就是说,狗R的根本没放弃线程时间片?

于是跟踪进去,发现Sleep(0)断点瞬间继续,说明放弃了又被调度了!!

查MSDN

ParametersdwMilliseconds [in]    The time interval for which execution is to be suspended, in milliseconds.    A value of zero causes the thread to relinquish the remainder of its time slice to any other thread that is ready to run. If there are no other threads ready to run, the function returns immediately, and the thread continues execution.    Windows XP:  A value of zero causes the thread to relinquish the remainder of its time slice to any other thread of equal priority that is ready to run. If there are no other threads of equal priority ready to run, the function returns immediately, and the thread continues execution. This behavior changed starting with Windows Server 2003.    A value of INFINITE indicates that the suspension should not time out.

也就是说xp下是相同优先级的线程,而之后就是所有的线程,只要没有等待运行的线程就会立刻继续当前线程


之前在单核和双核的时代,线程几乎是超过核心数的运行

现在四核下由于空闲核心多,导致线程一直被调度,最后表现出来是没有放弃时间片

同样的,SleepEx和SwitchToThread也是一样的道理


难道用GetMessage?当然可以

不过也可以多加一行代码

while(True){// 获取线程消息和窗口消息dwRet = WaitMessage();dwRet = PeekMessageA(&stMsg, NULL, 0, 0, PM_REMOVE);// PM_NOYIELDif(dwRet == FALSE){Sleep(0);continue;// 没有消息, 放弃当前线程时间片, 继续循环}if(stMsg.message == WM_QUIT){break;// 退出消息}...

整个世界,安静了.

不过这一爆的余波不小,我一下子想到POCT项目催生的游侠基础类库,急忙翻看窗口类的消息循环

发现当时赶时间,偷懒用了GetMessage(),反正完成相同的工作效果,偷懒就是一种艺术


那么是不是PeekMessage就是为了文档简单呢

不是,如果放到游戏开发,需要尽可能使用CPU资源的时候,你是打死都不想要GetMessage的

这时候只要把WaitMessage删掉,注释掉即可,当然循环流程最好重构,比如改为1秒Peek一次,以提升主要事务时间占用率

腾讯从韩国买来的次品穿越火线开始的时候为什么切换桌面会卡个大半年(其实我估计现在CF的陈年老伤一直都在)

因为他们在游戏循环里都没有处理Windows消息,导致线程没有响应(键盘鼠标是DirectX驱动处理的,游戏里不影响)


微软老一辈工程师辛辛苦苦设计的架构,本来是很强大的东西,

大多数都被浪费了,就好像Office VBA


1 0
原创粉丝点击