Qt Event Loop

Table of Contents

1. Qt Event Loop

1.1. Overview

1.1.1. QThread

class MyThread : public QThread {
  public:
    void run() {
        qDebug() << "my thread run: " << currentThreadId();
        exec();
    }
};
  1. myThread 需要在 run 里执行 exec 才能进入 event loop
  2. thread 默认的实现是 void run() {exec();}, 所以 QThread 默认会进入 event loop

1.1.2. QObject::moveToThread

SimpleObject obj;
obj.moveToThread(&myThread);

SimpleObject obj2(&obj);
obj2.moveToThread(&thread);
  1. moveToThread 可以把 qobject 与 thread 绑定, obj 对应的 slot 会通过 event 的形式在相应的 thread 执行
  2. obj2 的 move 会失败, 因为整个 qobject 树的 object 必须在绑定到同一个 thread, 因为涉及到 object 释放的问题

1.1.3. Timer

QTimer timer;
QObject::connect(&timer, &QTimer::timeout, &obj, &SimpleObject::handleEvent);
timer.start(1000);

Timer 是通过直接向 thread event loop 注册 timer 实现的, 并不需要单独的线程

1.1.4. QCoreApplication::processEvents

QTimer timer2;
timer2.setSingleShot(true);
timer2.callOnTimeout(&obj, &SimpleObject::handleLongEvent);
timer2.start(2000);
class SimpleObject : public QObject {
    Q_OBJECT

    void handleLongEvent() {
        while (true) {
            qDebug() << "handle long time event";
            QCoreApplication::processEvents();
            QThread::sleep(5);
        }
    }
};

handleLongEvent 会长时间运行以致阻塞主线程的 event loop, 所以 handleLongEvent 中需要通过 QCoreApplication::processEvents 来强行处理event

1.1.5. QCoreApplication::postEvent

QCoreApplication::postEvent(&obj, new QEvent(QEvent::None));
class SimpleObject : public QObject {
    Q_OBJECT

    bool event(QEvent *event) {
        qDebug() << "got event" << event->type();
        return QObject::event(event);
    }
};

postEvent 可以向 event loop 直接发送一个特定的 event, QObject::event() 负责处理 event

1.1.6. QEventLoop

QCoreApplication::exec 和 QThread::exec 最终都是通过 QEventLoop::exec 进行 event loop 的

1.2. Event vs. Signal

  1. 若 connect 使用了 Qt::DirectConnection 参数, 则 slot 是直接调用 metacall 实现的
  2. 若 connect 使用了 Qt::QueuedConnection 参数, 则 slot 是通过 post 一个 event, 由 event loop 来调用 metacall 实现的.
  3. 若 connect 使用了 Qt::AutoConnection 参数, 则 qt 自己选择使用 DirectConnection 还是 QueuedConnection, 具体的, 若 sender 和 receiver 在同一个线程, 则使用 DirectConnection, 否则使用 QueuedConnection

1.3. exec

QCoreApplication::exec
  eventLoop.exec()
    while (!d->exit.loadAcquire()):
      processEvents(flags | WaitForMoreEvents | EventLoopExec);
        data->eventDispatcher.loadRelaxed()->processEvents(flags);

EventDispatcher::processEvents :
  QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
    // postEventList !!
    while (i < data->postEventList.size()): 
      const QPostEvent &pe = data->postEventList.at(i); ++i;
      QEvent *e = pe.event;
      QObject * r = pe.receiver;
      QCoreApplication::sendEvent(r, e);
        notifyInternal2(receiver, event);
          doNotify(receiver,event)
            notify_helper(receiver, event)
              if (sendThroughObjectEventFilters(receiver, event)): 
                filtered = true;
                return filtered;
              // deliver the event
              consumed = receiver->event(event);

  d->pollfds.append(d->threadPipe.prepare());
  switch (qt_safe_poll(d->pollfds.data(), d->pollfds.size(), tm)): 

1.4. postEvent

QCoreApplication::postEvent
  // postEventList !!
  data->postEventList.addEvent(QPostEvent(receiver, event, priority));
  dispatcher->wakeUp();
    eventfd_write(fds[0], value)

1.5. sendEvent

sendEvent 会直接调用到对方的 event(), 是需要经过 event loop.

bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
  notifyInternal2(receiver, event);
  // ...
  consumed = receiver->event(event);

Author: [email protected]
Date: 2020-01-03 Fri 00:00
Last updated: 2020-01-07 Tue 19:04

知识共享许可协议