Qt 實(shí)現(xiàn)SendMessage和PostMessage機(jī)制

引言

  • 在VC編程中,需要往一個(gè)窗口發(fā)送消息時(shí),常常用到SendMessage和PostMessage。其中SendMessage是阻塞式的,發(fā)送消息后,立即響應(yīng)消息,響應(yīng)完成后才繼續(xù)往下執(zhí)行。而PostMessage是非阻塞式的,發(fā)送的消息會(huì)放到消息隊(duì)列后,立即返回繼續(xù)往下執(zhí)行。SendMessage和PostMessage都無(wú)需保證線程一致性。即,可以無(wú)論在工作線程中還是主線程中,都能向指定的窗口發(fā)送消息。
  • 在Qt編程中,很自然的會(huì)想到sendEvent和postEvent來(lái)實(shí)現(xiàn)VC這種消息發(fā)送機(jī)制。sendEvent是阻塞式的,發(fā)送事件后,立即對(duì)事件做出響應(yīng)。而postEvent則是非阻塞式的,發(fā)送的事件會(huì)放到事件隊(duì)列后,立即返回繼續(xù)往下執(zhí)行。需要注意的是,sendEvent必須滿(mǎn)足線程一致性,即必須保證事件接收對(duì)象所駐留的線程與sendEvent的調(diào)用線程保持一致。而postEvent無(wú)需保證這一點(diǎn)。
  • 因此,可以直接使用postEvent實(shí)現(xiàn)PostMessage機(jī)制。而在事件接收對(duì)象所駐留的線程與sendEvent的調(diào)用線程是同一個(gè)線程時(shí),可以使用sendEvent來(lái)實(shí)現(xiàn)SendMessage機(jī)制。但是當(dāng)sendEvent的調(diào)用線程與事件接收對(duì)象所駐留的線程不一致時(shí),這里需要借助postEvent和QEventLoop共同完成。
  • QEventLoop類(lèi)為我們提供了一種進(jìn)入和退出一個(gè)事件循環(huán)的方法。在任何時(shí)候,你都可以創(chuàng)建一個(gè)QEventLoop實(shí)例,然后調(diào)用exec()來(lái)啟動(dòng)一個(gè)事件循環(huán)實(shí)現(xiàn)阻塞效果。在這個(gè)循環(huán)期間,可以調(diào)用exit()來(lái)強(qiáng)制使exct()返回。

實(shí)現(xiàn)方案

#pragma once
#include <QObject>
#include <QEvent>
#include <QVariant>

void QPostMessage(QObject* obj, int msg, QVariant params);
long QSendMessage(QObject* obj, int msg, QVariant params);


static long s_rst = 0;
class LEvent : public QObject, public QEvent
{
public:
    LEvent(int msg, const QVariant params = QVariant(), long& rst = s_rst);
    ~LEvent();

public:
    static Type m_eventType;

    int m_message = 0;
    const QVariant& m_params;
    long& m_rst;
};
#include "LEvent.h"

QEvent::Type LEvent::m_eventType = (QEvent::Type)QEvent::registerEventType();

LEvent::LEvent(int msg, const QVariant params/* = QVariant()*/, long& rst/* = s_rst*/)
    : QEvent(m_eventType), m_message(msg), m_params(params), m_rst(rst)
{

}

LEvent::~LEvent()
{
}

void QPostMessage(QObject* obj, int msg, QVariant params)
{
    QCoreApplication::postEvent(obj, new LEvent(msg, params));
}

long QSendMessage(QObject* obj, int msg, QVariant params)
{
    long lResult = 0;
    if (obj->thread() == QThread::currentThread())
    {
        //事件隊(duì)列不會(huì)刪除事件。通常的方法是在棧上創(chuàng)建事件
        LEvent e(msg, params, lResult);
        QCoreApplication::sendEvent(obj, &e);
    }
    else
    {
        //事件隊(duì)列將獲得事件的所有權(quán)并在事件發(fā)送后刪除它。必須在堆上創(chuàng)建事件     
        LEvent* e = new LEvent(msg, params, lResult);
        QCoreApplication::postEvent(obj, e, Qt::HighEventPriority);

        QEventLoop loop;
        // 當(dāng)事件處理完畢后,事件實(shí)例會(huì)被銷(xiāo)毀,通知loop退出事件循環(huán)
        QObject::connect(e, SIGNAL(destroyed(QObject*)), &loop, SLOT(quit()));
        loop.exec(QEventLoop::ExcludeUserInputEvents);
    }
    return lResult;
}

參考文章:
https://note.youdao.com/ynoteshare1/index.html?id=79b2bd18897960d901608f1d219e56f0&type=note

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容