首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

利用 Windows 訊息迴圈實現訊息延遲合併:高效事件處理模式

     編輯:關於VC++

問題背景:高頻事件的處理困境

某計算模組面臨高頻「計算訊息」衝擊:若逐一處理,CPU 過載;若設固定時間間隔忽略,間隔長漏算、間隔短無效。傳統定時器方案因無法預知最佳間隔而失效。

核心思想:訊息合併器 + 視窗訊息隊列

設計 CFepMergeMessage 工具類:

  1. 訊息到達時不立即處理,僅儲存參數。
  2. 若為首個訊息,向自己 PostMessage 一個處理訊息 (WM_PROCESS_MESSAGE)。
  3. 後續訊息到達時合併參數 (同類同參數覆蓋)。
  4. 處理訊息被取出執行時,一次性批次處理 所有合併參數,執行真實回調。

利用 Windows 訊息隊列天然的串行化與延遲特性,完美解決突發高頻事件的合併處理。

完整實現代碼 (MFC 版)

標頭檔:FepMergeMessage.h

#pragma once
#include <afx.h>

class MERGE_MES_HEADER : public CObject {
    DECLARE_DYNAMIC(MERGE_MES_HEADER)
public:
    typedef void (*MergeMesCallBack)(const MERGE_MES_HEADER* lpMergMes);
    MergeMesCallBack pfnCallBack;           // 真實處理回調
    virtual MERGE_MES_HEADER* Clone() const = 0;      // 參數深拷貝
    virtual BOOL IsSameMes(const MERGE_MES_HEADER* pMes) const = 0; // 同一性判斷
};

class CFepMergeMessage : public CDialog {
public:
    CFepMergeMessage();
    virtual ~CFepMergeMessage();
    static BOOL Excute(const MERGE_MES_HEADER* pParam); // 靜態入口

protected:
    BOOL Create();                              // 建立隱藏對話框
    afx_msg LRESULT OnMergeMessage(WPARAM, LPARAM);  // 合併訊息處理
    afx_msg LRESULT OnProcessMessage(WPARAM, LPARAM); // 批次處理訊息
    DECLARE_MESSAGE_MAP()

    CArray<MERGE_MES_HEADER*, MERGE_MES_HEADER*> m_rgMessage; // 參數隊列
    BOOL m_bMessagePosted;                      // 是否已 Post 處理訊息
};

實作檔:FepMergeMessage.cpp

#include "StdAfx.h"
#include "FepMergeMessage.h"
#include "../Include/FepPtr.h"

#define WM_MERGE_MESSAGE  WM_USER + 100
#define WM_PROCESS_MESSAGE WM_USER + 200

IMPLEMENT_DYNAMIC(MERGE_MES_HEADER, CObject)

static CFepPtr<CFepMergeMessage> pDlgMergeMessage(NULL);

CFepMergeMessage::CFepMergeMessage() : m_bMessagePosted(FALSE) { Create(); }

CFepMergeMessage::~CFepMergeMessage() {
    for (int i = 0; i < m_rgMessage.GetSize(); ++i) delete m_rgMessage.GetAt(i);
    m_rgMessage.RemoveAll();
}

BEGIN_MESSAGE_MAP(CFepMergeMessage, CDialog)
    ON_MESSAGE(WM_MERGE_MESSAGE, OnMergeMessage)
    ON_MESSAGE(WM_PROCESS_MESSAGE, OnProcessMessage)
END_MESSAGE_MAP()

BOOL CFepMergeMessage::Create() {
    if (GetSafeHwnd()) return TRUE;
    DWORD dwDlgTempl[100] = {0};
    DLGTEMPLATE* pDlgTmp = (DLGTEMPLATE*)dwDlgTempl;
    pDlgTmp->style = WS_POPUP | DS_CONTROL;
    pDlgTmp->cx = 1; pDlgTmp->cy = 1;
    return CDialog::CreateIndirect(pDlgTmp);
}

BOOL CFepMergeMessage::Excute(const MERGE_MES_HEADER* pParam) {
    if (!pDlgMergeMessage.m_pPtr) pDlgMergeMessage.m_pPtr = new CFepMergeMessage;
    if (!pDlgMergeMessage->GetSafeHwnd()) return FALSE;
    pDlgMergeMessage->SendMessage(WM_MERGE_MESSAGE, (WPARAM)pParam, 0);
    return TRUE;
}

LRESULT CFepMergeMessage::OnMergeMessage(WPARAM wParam, LPARAM) {
    const MERGE_MES_HEADER* pNewMes = (const MERGE_MES_HEADER*)wParam;
    // 合併邏輯:同類型且 IsSameMes 為真則替換
    for (int i = 0; i < m_rgMessage.GetSize(); ++i) {
        MERGE_MES_HEADER* pMes = m_rgMessage.GetAt(i);
        if (pNewMes->IsKindOf(pMes->GetRuntimeClass()) && pNewMes->IsSameMes(pMes)) {
            delete pMes;
            m_rgMessage.RemoveAt(i);
            --i;
        }
    }
    m_rgMessage.Add(pNewMes->Clone()); // 儲存副本
    if (!m_bMessagePosted) {
        PostMessage(WM_PROCESS_MESSAGE, 0, 0); // 排程批次處理
        m_bMessagePosted = TRUE;
    }
    return 0;
}

LRESULT CFepMergeMessage::OnProcessMessage(WPARAM, LPARAM) {
    while (m_rgMessage.GetSize() > 0) {
        MERGE_MES_HEADER* pHeader = m_rgMessage.GetAt(0);
        pHeader->pfnCallBack(pHeader); // 執行真實回調
        delete pHeader;
        m_rgMessage.RemoveAt(0);
    }
    m_bMessagePosted = FALSE;
    return 0;
}

使用者自訂參數類別設計規範

每個使用此工具的模組必須繼承 MERGE_MES_HEADER 並實作三大核心:

成員類型職責
pfnCallBack函數指標真正的業務處理邏輯
Clone()純虛函數深拷貝自身,供隊列儲存副本
IsSameMes()純虛函數判斷新訊息是否與現有訊息「同一」 (可合併)

使用範例

// 1. 定義具體參數類別
class CCalcMes : public MERGE_MES_HEADER {
public:
    int nParam1, nParam2;
    CCalcMes(int p1, int p2) : nParam1(p1), nParam2(p2) {
        pfnCallBack = &CCalcMes::DoCalc; // 靜態回調
    }
    virtual MERGE_MES_HEADER* Clone() const { return new CCalcMes(*this); }
    virtual BOOL IsSameMes(const MERGE_MES_HEADER* pMes) const {
        const CCalcMes* p = static_cast<const CCalcMes*>(pMes);
        return p && nParam1 == p->nParam1; // 同參數1視為同一訊息
    }
    static void DoCalc(const MERGE_MES_HEADER* pMes) {
        const CCalcMes* p = static_cast<const CCalcMes*>(pMes);
        // 真實計算邏輯...
    }
};

// 2. 觸發合併處理 (任意執行緒均可呼叫)
void OnDataArrive(int p1, int p2) {
    CCalcMes* pMes = new CCalcMes(p1, p2);
    CFepMergeMessage::Excute(pMes); // 靜態方法,線程安全
}

設計優勢總結

  • 零阻塞:呼叫端 SendMessage 同步投遞合併訊息,PostMessage 異步排程處理,不阻塞生產者。
  • 自動合併:同類同參數訊息自動去重,僅保留最新參數。
  • 線程安全:所有處理集中在 UI 執行緒 (隱藏對話框所在執行緒),天然避免競態條件。
  • 擴展性強:支援多種訊息類型共存,單例模式全局可訪問。
  • 資源安全:析構函數自動清理隊列殘留參數,Clone/Delete 配對管理記憶體。

此模式適用於所有「高頻觸發、結果冪等、需批次處理」的場景:即時繪圖刷新、日誌批次寫入、網路封包合併發送、感應器資料聚合等。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved