门业网站模板ps做分享类网站效果图
2026/4/6 9:36:06 网站建设 项目流程
门业网站模板,ps做分享类网站效果图,绍兴网站建设报价,网站怎么弄序幕#xff1a;两个程序员的对话 小王#xff1a;老张#xff0c;我最近写了个管道通信程序#xff0c;异步I/O发送数据#xff0c;但UI会冻结#xff0c;怎么办#xff1f; 老张#xff1a;哦#xff0c;这是经典的Windows编程问题。你用了MsgWaitForMultipleObject…序幕两个程序员的对话小王老张我最近写了个管道通信程序异步I/O发送数据但UI会冻结怎么办老张哦这是经典的Windows编程问题。你用了MsgWaitForMultipleObjects吗小王用了啊但还是有问题…第一幕初识消息等待的陷阱老张先看看你的代码结构小王while(等待I/O){resultMsgWaitForMultipleObjects(...,QS_ALLINPUT);if(有消息){PeekMessage(msg,...);// 取一条DispatchMessage(msg);// 处理一条}}老张问题就在这里MsgWaitForMultipleObjects返回有消息只意味着队列非空。如果队列有10条消息你只处理1条就回去等待系统立即又告诉你有消息你就陷入消息循环永远不检查I/O了小王啊那怎么办老张必须清空队列if(有消息){while(PeekMessage(msg,...)){// 处理所有消息TranslateMessage(msg);DispatchMessage(msg);}// 清空后再重新评估I/O状态}第二幕隐藏的优先级反转小王我加了while循环但新问题来了用户拖动窗口时消息太多处理太久I/O超时了老张这就是优先级反转——低优先级消息处理阻塞了高优先级I/O检查。Windows消息机制有几个关键特性消息是异步产生的用户操作可能瞬间产生几十条消息MsgWait只是检测器它不关心消息处理要花多少时间事件可能被错过如果事件在消息处理期间触发可能就丢失了第三幕消息丢失的九种情形老张说到丢失让我详细说说MsgWaitForMultipleObjects可能丢消息的几种情况情况一队列未清空老张这是最常见的。比如用户快速点击按钮产生[点击1][点击2][点击3]三条消息。你只处理第一条就回去等待系统立刻又报告有消息…小王然后就忘了检查I/O情况二时间窗口的竞争老张想象一个精确定时场景时间轴 0ms: 开始等待超时设为1000ms 999ms: 消息到达队列 1000ms: 超时发生小王MsgWait会返回什么老张可能返回WAIT_TIMEOUT消息虽然到了但超时也到了系统优先报告超时。情况三标志不完整小王我用了QS_KEY | QS_MOUSE只关心键盘鼠标。老张那WM_PAINT、WM_TIMER呢这些消息会被积压最终导致UI不响应。更糟的是有些消息是链式反应的WM_SIZE → 触发WM_PAINT → 触发更多重绘漏掉一个后续都受影响。情况四过滤器的副作用老张你用PeekMessage时设置过滤器了吗小王有时会过滤特定消息。老张危险比如PeekMessage(msg,hWnd,0,0,PM_REMOVE);// 只处理特定窗口但对话框、子窗口、系统全局消息都被忽略了。情况五多对象等待的随机性小王如果同时等待多个事件呢老张HANDLE events[2]{ioEvent,userEvent};resultMsgWaitForMultipleObjects(2,events,...);如果ioEvent和消息同时就绪可能返回WAIT_OBJECT_0事件也可能返回WAIT_OBJECT_02消息不确定情况六GetMessage的阻塞陷阱小王我见过有人用GetMessage代替PeekMessage。老张大忌GetMessage会阻塞在阻塞期间I/O完成事件可能发生又被重置其他消息继续堆积可能永远等不到特定消息情况七WM_PAINT的惰性老张WM_PAINT消息很特殊。系统告诉你有PAINT消息但实际调用PeekMessage时可能取不到完整消息情况八线程消息的隐蔽性小王线程消息有什么区别老张PostThreadMessage发送的消息需要用QS_POSTMESSAGE标志才能检测到。用QS_ALLINPUT可能漏掉情况九句柄过滤的盲区老张如果你只处理主窗口消息那么工具提示消息上下文菜单消息COM激活消息都可能被忽略。第四幕构建健壮的解决方案小王这么多坑到底怎么写才安全老张记住这几个原则原则一有界处理// 每次最多处理N条消息constintMAX_MSGS20;intprocessed0;while(processedMAX_MSGSPeekMessage(msg,...)){// 处理消息processed;}// 处理后必须重新检查I/O事件原则二定期检查事件老张在消息循环中要穿插检查I/O状态while(处理消息){// 每处理几条消息就检查一次if(processed%50){if(WaitForSingleObject(ioEvent,0)WAIT_OBJECT_0){// I/O已完成立即跳出break;}}}原则三完整标志集老张不要吝啬标志DWORD wakeMaskQS_ALLINPUT|QS_ALLPOSTMESSAGE;// 或者至少DWORD wakeMaskQS_ALLEVENTS;// 比QS_ALLINPUT更完整原则四正确处理退出老张WM_QUIT是特殊消息if(msg.messageWM_QUIT){// 不能简单地DispatchMessage// 要放回队列让主循环处理PostQuitMessage((int)msg.wParam);return;// 优雅退出}第五幕完整的实现示例老张结合所有原则一个健壮的实现应该是这样的classRobustAsyncIOWaiter{public:enumWaitResult{IO_COMPLETED,TIMEOUT,USER_CANCELLED,ERROR_OCCURRED};WaitResultWaitForIOWithMessages(HANDLE ioEvent,DWORD timeoutMs){// 1. 记录开始时间DWORD startTickGetTickCount();DWORD remainingtimeoutMs;while(true){// 2. 使用完整的事件掩码DWORD wakeMaskQS_ALLEVENTS|QS_ALLPOSTMESSAGE;// 3. 等待事件或消息DWORD resultMsgWaitForMultipleObjects(1,ioEvent,FALSE,// 等待任意一个remaining,wakeMask);// 4. 处理各种结果switch(result){caseWAIT_OBJECT_0:// I/O完成事件returnProcessIOCompletion(ioEvent);caseWAIT_OBJECT_01:// 有消息到达if(!ProcessMessageBatch(ioEvent,20,50)){// 处理过程中检测到取消returnUSER_CANCELLED;}break;caseWAIT_TIMEOUT:returnTIMEOUT;caseWAIT_FAILED:returnERROR_OCCURRED;default:// 处理异常情况LogUnexpectedWaitResult(result);returnERROR_OCCURRED;}// 5. 重新计算剩余时间DWORD elapsedGetTickCount()-startTick;if(elapsedtimeoutMs){returnTIMEOUT;}remainingtimeoutMs-elapsed;}}private:boolProcessMessageBatch(HANDLE ioEvent,intmaxMessages,DWORD maxTimeMs){DWORD startTimeGetTickCount();intprocessed0;MSG msg;while(processedmaxMessages){// 检查时间限制if(GetTickCount()-startTimemaxTimeMs){break;// 时间到了}// 优先检查I/O事件if(WaitForSingleObject(ioEvent,0)WAIT_OBJECT_0){returnfalse;// I/O已完成让外层处理}// 取消息非阻塞if(!PeekMessage(msg,NULL,0,0,PM_REMOVE)){break;// 队列已空}// 特殊处理退出消息if(msg.messageWM_QUIT){// 将退出消息重新排队PostQuitMessage((int)msg.wParam);returnfalse;// 通知外层需要退出}// 正常处理if(msg.messageWM_KEYFIRSTmsg.messageWM_KEYLAST){TranslateMessage(msg);}DispatchMessage(msg);processed;}returntrue;// 继续等待}WaitResultProcessIOCompletion(HANDLE ioEvent){// 获取I/O结果DWORD bytesTransferred0;if(GetOverlappedResult(pipe,overlapped,bytesTransferred,FALSE)){returnIO_COMPLETED;}else{returnERROR_OCCURRED;}}};第六幕架构的终极反思小王这么复杂有没有更简单的方法老张有问题的根源在于把UI线程和I/O等待耦合。现代Windows编程应该方案一I/O完成端口// 专用I/O线程DWORD WINAPIIOThreadProc(LPVOID){while(true){GetQueuedCompletionStatus(port,...);// 处理I/O通过消息或回调通知UI}}方案二线程池// 提交I/O工作项SubmitThreadpoolWork(work);// 回调函数在线程池执行方案三基于事件的异步模式// 使用现代异步模式async_resultco_awaitasync_write(pipe,data);// UI线程完全不被阻塞小王那我该用哪个老张根据场景选择简单应用用我们讨论的有界消息处理高性能服务用I/O完成端口现代应用用C20协程或WinRT异步终幕核心原则总结老张最后记住这六条黄金法则清空但有限处理消息要清空队列但要设置边界穿插检查消息处理中要定期检查I/O状态完整标志使用完整的等待标志集特殊处理对WM_QUIT等特殊消息单独处理超时重算每次循环重新计算剩余时间考虑分离复杂的I/O操作考虑使用单独线程小王我明白了关键是理解Windows消息机制的异步本质和MsgWaitForMultipleObjects的检测特性。老张正是。Windows编程就像走钢丝在UI响应性和I/O及时性之间寻找平衡。掌握了这些原则你就能写出既流畅又可靠的应用程序。这场对话后小王重构了他的代码应用了有界消息处理和定期I/O检查程序再也没有出现过UI冻结或I/O超时的问题。更重要的是他学会了在遇到复杂问题时从架构层面思考更优雅的解决方案。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询