Qt事件循环(Event Loop)

        作为信号和槽的得力干将之一,事件循环负责接收和分发事件,它同时支持线程内事件处理和跨线程事件处理,它有一个主事件循环负责维持主进程的持续运行。

目录

Qt程序永生不死的秘密:

牛马们

牛马们偷懒的秘诀

领导视察工作


Qt程序永生不死的秘密:

        通常情况下,Qt应用程序的main函数中都会出现 QApplication和 exec :

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);//或者QGuiApplication、QCoreApplication

    //any other code

    return app.exec();
}

        QApplication app(argc, argv);创建并初始化了一个 QApplication 对象,设置一些线程相关的数据。

        app.exec();这一步才是真正的开始了一个事件循环,它创建了一个 QEventLoop 对象,正是这个对象处理了程序中Qt的所有事件:

int QCoreApplication::exec()
{
    QEventLoop eventLoop;
    ……
    int returnCode = eventLoop.exec();
    ……
    return returnCode;
}

 eventLoop.exec()中是一个无限循环,所以程序才不会立即退出,这就是它长生不死的秘密:

int QEventLoop::exec(ProcessEventsFlags flags = AllEvents))
{
    Q_D(QEventLoop);
    ……
    while (!d->exit.loadAcquire())
        processEvents(flags | WaitForMoreEvents | EventLoopExec);
    ……
    return d->returnCode.load();
}

但是!可是!可但是!最终完成事件分发的却是事件调度器,可以看出,事件调度器调用的是每个线程各自专属的线程调度器,简而言之,就是一群牛马藏在角落替所有人负重前行。。。:

bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
    if (!d->threadData->hasEventDispatcher())
        return false;
    return d->threadData->eventDispatcher.load()->processEvents(flags);
}

牛马们

        事件分发器已经如此辛劳,我们应当多了解它一下,避免同样成为牛马的结局,每个系统下的牛马都不一样,我们以UNIX的牛马为例:

bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
{
    Q_D(QEventDispatcherUNIX);
    ……
    QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
    ……
    timespec *tm = nullptr;
    ……
    d->pollfds.append(d->threadPipe.prepare());

    int nevents = 0;

    switch (qt_safe_poll(d->pollfds.data(), d->pollfds.size(), tm)) {
    ……
    }
    return (nevents > 0);
}

void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type,
                                               QThreadData *data)
{
    ……
    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);
    }
    ……
}

        可以看到 sendPostedEvents(QObject *receiver, int event_type,QThreadData *data) 会分发当前队列中阻塞的所有事件。

牛马偷懒的秘诀

        当sendPostedEvents执行完之后,如果立即返回并进入下一个事件分发循环,那牛马不就累死了吗?很显然,牛马们并不傻,它们先是通过调用 eventfd 获取了一个文件描述符,并在qt_safe_poll中调用系统底层API,在UNIX中,其调用的是select函数,并把fd给到select,然后当select接收到特定信号后会退出阻塞状态,牛马们接着继续干活。这活儿不一下子就移花接木到系统手里了吗?不得不说,这一招,连笔者都觉得妙啊,不仅不用干活,资源占用还低,被发现摸鱼的概率也极低!

领导视察工作

        当领导(QApplication::processEvents())来视察工作的时候,牛马们又会开始奋力干活,没办法,谁家里没有一老一小呢:

void QMyClass::other_func()
{
    QApplication::processEvents();//领导
}
//牛马加班
void QApplication::processEvents(QEventLoop::ProcessEventsFlags flags)
{
    QThreadData *data = QThreadData::current();
    if (!data->hasEventDispatcher())
        return;
    data->eventDispatcher.load()->processEvents(flags);
}

        玩笑归玩笑,QApplication::processEvents() 的作用是立即处理堆积在事件列队中的所有事件,它对我们来说是一个非常有用的函数,当我们不得不在主线程做一些需要长时间等待的任务,而又希望主界面正常使用时,我们就可以每等待10ms执行一次QApplication::processEvents() 确保Qt事件正常分发,主界面刷新不会卡死。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值