QT sendEvent()和postEvent()

本文深入解析了QT中sendEvent和postEvent两种事件处理方式的区别。sendEvent通过同步方式直接调用接收者的事件处理函数,而postEvent则将事件异步添加到事件队列中等待后续处理。

QT文档中这样解释:

sendEvent(QObject* receiver,QEvent* event)

使用notify()函数直接给receiver发送事件。

postEvent(QObject* receiver, QEvent* event)

向事件队列中添加receiver和event。

简单说,sendEvent使用的是同步处理事件,postEvent使用的异步处理事件


sendEvent代码分析

[cpp]  view plain  copy
  1. inline bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)  
  2. {  if (event) event->spont = falsereturn self ? self->notifyInternal(receiver, event) : false; }  

直接调用notifyInternal接口,注意中间设置自发消息标志位为false,同时还需要判断self是有有效(QCoreApplication是否启动)

[cpp]  view plain  copy
  1. bool QCoreApplication::notifyInternal(QObject *receiver, QEvent *event)  
  2. {  
  3.     // Make it possible for Qt Jambi and QSA to hook into events even  
  4.     // though QApplication is subclassed...  
  5.     bool result = false;  
  6.     void *cbdata[] = { receiver, event, &result };  
  7.     if (QInternal::activateCallbacks(QInternal::EventNotifyCallback, cbdata)) {  
  8.         return result;  
  9.     }  
  10.   
  11.     // Qt enforces the rule that events can only be sent to objects in  
  12.     // the current thread, so receiver->d_func()->threadData is  
  13.     // equivalent to QThreadData::current(), just without the function  
  14.     // call overhead.  
  15.     QObjectPrivate *d = receiver->d_func();  
  16.     QThreadData *threadData = d->threadData;  
  17.     ++threadData->loopLevel;  
  18.   
  19.     bool returnValue = notify(receiver, event);  
  20.     --threadData->loopLevel;  
  21.     return returnValue;  
  22. }  
删除了一些不重要的代码,notifyInternal的主要作用是activateCallbacks,直接看notify

[cpp]  view plain  copy
  1. bool QCoreApplication::notify(QObject *receiver, QEvent *event)  
  2. {  
  3.     Q_D(QCoreApplication);  
  4.     // no events are delivered after ~QCoreApplication() has started  
  5.     if (QCoreApplicationPrivate::is_app_closing)  
  6.         return true;  
  7.     if (receiver == 0) {                        // serious error  
  8.         qWarning("QCoreApplication::notify: Unexpected null receiver");  
  9.         return true;  
  10.     }  
  11.     return receiver->isWidgetType() ? false : d->notify_helper(receiver, event);  
  12. }  
这个接口在QT文档上有注释。注意其中当receiver为控件时,不进行处理。

[cpp]  view plain  copy
  1. bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event)  
  2. {  
  3.     // send to all application event filters  
  4.     if (sendThroughApplicationEventFilters(receiver, event))  
  5.         return true;  
  6.     // send to all receiver event filters  
  7.     if (sendThroughObjectEventFilters(receiver, event))  
  8.         return true;  
  9.     // deliver the event  
  10.     return receiver->event(event);  
  11. }  

在这里可以看到事件是如何处理的:

  • 先送入Application的事件过滤器,看看是否在事件过滤器中处理
  • 再查看receiver是否有此事件的过滤器
  • 最后,将事件送入receiver的event接口。

从整个过程来看,可以认为sendEvent直接调用了receiver的event接口。因此,可以认为处理方式为同步处理方式。


PostEvent分析

[cpp]  view plain  copy
  1. void QCoreApplication::postEvent(QObject *receiver, QEvent *event)  
  2. {  
  3.     postEvent(receiver, event, Qt::NormalEventPriority);  
  4. }  
[cpp]  view plain  copy
  1. void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)  
  2. {  
  3.     ...  
  4.     QThreadData * volatile * pdata = &receiver->d_func()->threadData;  //得到线程信息  
  5.     QThreadData *data = *pdata;  
  6.     if (!data) {  
  7.         // posting during destruction? just delete the event to prevent a leak  
  8.         delete event;  
  9.         return;  
  10.     }  
  11.   
  12.     // lock the post event mutex  
  13.     data->postEventList.mutex.lock();  
  14.   
  15.     // if object has moved to another thread, follow it  
  16.     while (data != *pdata) {                        <span style="font-family: Arial, Helvetica, sans-serif;">//在这里判断receiver线程信息是否发生变化。(有可能是另外一个线程调用用receiver->moveToThread)</span>  
  17.         data->postEventList.mutex.unlock();  
  18.   
  19.         data = *pdata;  
  20.         if (!data) {  
  21.             // posting during destruction? just delete the event to prevent a leak  
  22.             delete event;  
  23.             return;  
  24.         }  
  25.   
  26.         data->postEventList.mutex.lock();  
  27.     }  
  28. //这里postEventList还是被锁着的。  
  29.     // if this is one of the compressible events, do compression  
  30.     if (receiver->d_func()->postedEvents  
  31.         && self && self->compressEvent(event, receiver, &data->postEventList)) {  
  32.         data->postEventList.mutex.unlock();//这个事件有可能被压缩(实际上是发现队列中有这个事件还没有被处理,且这个事件是可以被压缩的,例如paintevent)  
  33.         return;  
  34.     }  
  35.   
  36.     event->posted = true;  
  37.     ++receiver->d_func()->postedEvents;  
  38.     if (event->type() == QEvent::DeferredDelete && data == QThreadData::current()) {  
  39.         // remember the current running eventloop for DeferredDelete  
  40.         // events posted in the receiver's thread  
  41.         event->d = reinterpret_cast<QEventPrivate *>(quintptr(data->loopLevel)); //receiver即将被析构?  
  42.     }  
  43. //将事件添加到postEventList中,注意这里的优先级第一个最高,最后一个优先级最低  
  44.     if (data->postEventList.isEmpty() || data->postEventList.last().priority >= priority) {  
  45.         // optimization: we can simply append if the last event in  
  46.         // the queue has higher or equal priority  
  47.         data->postEventList.append(QPostEvent(receiver, event, priority));  
  48.     } else {  
  49.         // insert event in descending priority order, using upper  
  50.         // bound for a given priority (to ensure proper ordering  
  51.         // of events with the same priority)  
  52.         QPostEventList::iterator begin = data->postEventList.begin()  
  53.                                          + data->postEventList.insertionOffset,  
  54.                                    end = data->postEventList.end();  
  55.         QPostEventList::iterator at = qUpperBound(begin, end, priority);  
  56.         data->postEventList.insert(at, QPostEvent(receiver, event, priority));  
  57.     }  
  58.     data->canWait = false;  
  59.     data->postEventList.mutex.unlock();//在这里解除锁  
  60. //receiver所在的线程调用eventDispatcher处理postEventList  
  61.     if (data->eventDispatcher)  
  62.         data->eventDispatcher->wakeUp();  
  63. }  
从上面可以看出,postEvent实际上是将事件添加到receiver所在线程中的一个队列中,至于这个队列所在的线程什么时候处理这个事件,postEvent是无法控制的






### 事件处理方式 `sendEvent` 是同步事件处理方法,事件在调用时立即被派发处理,不进入事件队列。事件处理完成后函数才返回,因此支持在栈上或堆上分配的事件对象[^1]。相对地,`postEvent` 是异步事件处理方法,事件被放入接收者的事件队列中,等待事件循环在适当时机进行处理。调用 `postEvent` 后函数立即返回,不会阻塞当前线程。由于事件对象需要在事件队列中保存直到被处理,因此只能使用堆上分配的事件对象,并且事件处理完成后由 Qt 自动销毁[^4]。 ### 事件对象生命周期管理 `sendEvent` 允许使用栈上或堆上分配的事件对象,因为它是同步调用,事件对象的生命周期由调用者管理。在事件处理完成后,不需要额外释放资源。而 `postEvent` 只接受堆上分配的事件对象,因为事件需要在队列中等待处理。Qt 使用智能指针(如 `QScopedPointer`)来管理事件对象的生命周期,在事件处理完成后自动释放内存[^2]。 ### 线程安全性 `sendEvent` 不是线程安全的,必须在事件接收者的所属线程中调用。而 `postEvent` 是线程安全的,可以在任意线程中调用,事件会被排队并由接收者所在线程的事件循环处理。这使得 `postEvent` 成为跨线程通信的首选方式[^2]。 ### 事件触发时机 由于 `sendEvent` 是同步的,事件会立即被处理,因此可以获取处理结果。而 `postEvent` 是异步的,事件被放入队列,等待事件循环处理,因此事件不是实时触发的。这种方式适用于延迟处理或需要按顺序处理多个事件的场景[^1]。 ### 适用场景 `sendEvent` 适用于需要立即处理事件并获取结果的场景,例如在 UI 中触发按钮点击后立即更新状态。`postEvent` 适用于跨线程通信、延迟处理或需要确保事件按顺序处理的场景,例如在后台线程接收到数据后通知主线程更新界面[^4]。 ### 示例代码对比 以下代码展示了 `sendEvent` `postEvent` 的使用方式: ```cpp #include <QApplication> #include <QEvent> #include <QObject> #include <QDebug> class MyReceiver : public QObject { Q_OBJECT public: bool event(QEvent *e) override { if (e->type() == QEvent::User) { qDebug() << "Received event!"; return true; } return QObject::event(e); } }; int main(int argc, char *argv[]) { QApplication app(argc, argv); MyReceiver receiver; // 使用 sendEvent QEvent event(QEvent::User); app.sendEvent(&receiver, &event); // 立即处理 // 使用 postEvent QEvent *postEvent = new QEvent(QEvent::User); QApplication::postEvent(&receiver, postEvent); // 异步处理 return app.exec(); } ``` 在上述代码中,`sendEvent` 在调用后立即处理事件,而 `postEvent` 将事件放入队列,等待事件循环处理。 ### 总结 `sendEvent` `postEvent` 在事件处理方式、生命周期管理、线程安全性及触发时机上存在显著差异。`sendEvent` 适用于需要立即处理事件的场景,而 `postEvent` 更适合异步处理跨线程通信。理解这些区别有助于在实际开发中选择合适的事件处理方式。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值