WM_TIMER消息在线程被阻塞时的系统处理

本文通过实验探讨了当UI线程被阻塞时WM_TIMER消息的行为。结果显示,多个WM_TIMER消息可能被合并为一个,仅在线程恢复后处理一次。这有助于理解Windows消息队列在特定条件下的工作原理。
部署运行你感兴趣的模型镜像

原文在这里:http://www.cnblogs.com/hoodlum1980/archive/2010/08/20/1804913.html

我的脑海中忽然对这样一个问题有一些模糊,也就是当一个安装了定时器的线程被阻塞期间,定时器消息如何被送往消息队列?在线程从阻塞状态恢复以后,消息队列的状态是怎么样的?是否里面聚集多个WM_TIMER消息?还是阻塞期间没有收到WM_TIMER消息,还是在阻塞期间多个应该送达的WM_TIMER被合并成了一个?(类似WM_PAINT消息那样)。

    所以我做了一个小实验来验证这个问题,结果我发现结论是最后一种情况,即可能系统在被唤起应该像某个线程的消息队列投递WM_TIMER消息时,它如果发现消息队列中已经有相同的WM_TIMER消息(ID号相同),则可能放弃投递,否则才会投递。这样就符合我们观察到的结果,即阻塞期间应该产生的多个定时器消息看起来仿佛被合并成了一个。

    这个试验是这样的,我给UI线程安装一个5秒钟间隔的定时器(收到定时器消息后在窗口进行输出),然后发起另一个线程,阻塞 UI 线程21秒的时间。然后观察UI线程的输出,效果如下:

    

    

 

    我们绘制一个更直观的的图形来解释上面的输出,如下:

    

        

 

    图中,红色的箭头是UI线程实际收到的定时器消息,蓝色箭头是阻塞期间应该产生消息的时间。可见,在阻塞期间应该产生 4 个 WM_TIMER 消息,但实际上在线程从阻塞状态恢复后,立即处理了仅仅一条。此后消息仍然按照既定间隔定期发送。

 

    上文中用于测试的代码如下: 

CODE_WM_TIMER_TEST

Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->#include "stdafx.h"
#include <string>
#include "resource.h"
using namespace std;

HINSTANCE hInst;
string m_msg;
int blockTime;

LRESULT CALLBACK MyDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
//测试线程
DWORD WINAPI TestThread(void* pArg);

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
     // TODO: Place code here.
    hInst = hInstance;
    DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_DIALOG1), NULL, (DLGPROC)MyDlgProc, 0);
    return 0;
}

// 对话框
LRESULT CALLBACK MyDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    static int b_timerOn;
    static int timerNum;
    switch(message)
    {
    case WM_INITDIALOG:
        b_timerOn = 0;
        timerNum = 0;
        break;
    case WM_COMMAND:
        {
            WORD ctl = LOWORD(wParam);
            switch(ctl)
            {
            case IDOK:
            case IDCANCEL:
                EndDialog(hDlg, ctl);
                return TRUE;
            case IDC_BT_SETTIMER:
                if(b_timerOn)
                {
                    SetDlgItemText(hDlg, ctl, "开始计时器");
                    KillTimer(hDlg, 1);
                }
                else
                {
                    SetDlgItemText(hDlg, ctl, "停止计时器");
                    SetTimer(hDlg, 1, 5000, NULL);
                }
                b_timerOn ^= 1; //取反(在0,1之间切换)
                return TRUE;

            case IDC_BT_STARTTHREAD:
                {
                    DWORD threadId;
                    SYSTEMTIME st;
                    blockTime = 21000;
                    char line[128];
                
                    HANDLE hThread = CreateThread(NULL, 0,
                        TestThread, 
                        (LPVOID)&blockTime,
                        0, //立即执行
                        &threadId
                        );

                    //sprintf(line, "thread: %ld start...\r\n", threadId);
                    //m_msg += line;
                    //SetDlgItemText(hDlg, IDC_MSG, m_msg.c_str());

                    //阻塞主线程
                    WaitForSingleObject(hThread, INFINITE);
                    CloseHandle(hThread);

                    GetLocalTime(&st);
                    sprintf(line, "%02d:%02d: thread: %ld exit...\r\n", st.wMinute, st.wSecond, threadId);
                    m_msg += line;
                    SetDlgItemText(hDlg, IDC_MSG, m_msg.c_str());
                }
                return TRUE;
            }
        }
        break;
    case WM_TIMER:
        {
            SYSTEMTIME st;
            char text[96];
            GetLocalTime(&st);
            sprintf(text, "%02d:%02d WM_TIMER_%04ld\r\n", st.wMinute, st.wSecond, timerNum);
            timerNum++;

            m_msg = m_msg + text;
            SetDlgItemText(hDlg, IDC_MSG, m_msg.c_str());
        }
        return TRUE;
    case WM_DESTROY:
        KillTimer(hDlg, 1);
        return TRUE;
    }
    return FALSE;
}



//测试线程
DWORD WINAPI TestThread(void* pArg)
{
    int* pBlockTime = (int*)pArg;
    Sleep(*pBlockTime);
    return 0;
}


 

    结论:根据上面的观察,可以认为,定时器消息和 绘制消息类似,在进程被阻塞期间,多个定时器消息可能被系统透明的合并成了一条消息。在从阻塞状态恢复后,定时器扔按照原有间隔继续发送。

 

    请注意,不要认为所有WM_TIMER会严格按照响应时间产生(即不要认为一定能产生相应的数量),不要认为每一条一定会在某个时刻得到处理(即其被处理的时间也取决于线程的运行状态,例如被阻塞所拖延)。可以认为在阻塞期间的所有定时器消息仅会在线程停止阻塞后处理一次。

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

‌1. MFC对话框主题风格动态修改问题‌ ‌可行性验证‌: 模态/非模态对话框的视觉风格可通过CMFCVisualManager动态调整,但需注意: cpp Copy Code // 在对话框显示后修改主题(需包含头文件afxmetafile.h) CMFCVisualManagerOffice2007* pManager = new CMFCVisualManagerOffice2007; CMFCVisualManager::SetDefaultManager(pManager); ‌重绘必要性‌:修改主题后必须调用Invalidate(FALSE)强制重绘,UpdateWindow()用于立即刷新窗口。若未生效,检查是否重写了OnPaint()或存在自定义绘制逻辑。 ‌风险提示‌: 混合使用主题风格可能导致视觉不一致(如主对话框与子对话框控件颜色差异) 部分第三方控件库可能不支持运行主题切换 ‌2. Windows消息处理机制深度解析‌ ‌消息队列与阻塞‌: ‌消息队列容量‌:每个线默认有1000条消息的队列空间(可通过PeekMessage的PM_REMOVE参数调整) ‌灰屏现象‌:若消息处理超过系统阈值(通常5秒),Windows会触发WM_APPCOMMAND消息显示未响应提示 ‌消息丢弃条件‌: 队列满消息被丢弃(如高频WM_TIMER消息) 调用PostMessage若目标窗口句柄无效,消息直接丢弃 ‌调试建议‌: 使用Spy++工具监控消息队列,检查是否存在WM_QUIT等终止消息堆积 ‌3. Windows系统稳定性保障机制‌ ‌容错设计‌: ‌消息优先级‌:WM_PAINT、WM_QUIT等关键消息永远优先处理 ‌看门狗机制‌:系统级线会检测应用响应状态,超则触发WM_DESTROY ‌蓝屏预防‌: 单线消息处理不会导致系统崩溃 内存泄漏检测通过HeapWalk实现,临界错误会触发EXCEPTION_ACCESS_VIOLATION ‌性能优化‌: 对耗操作使用PostThreadMessage投递到后台线 避免在OnPaint中执行复杂计算,改用BeginPaint/EndPaint块
最新发布
10-06
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值