QT 源码分析 - exec 函数

目录

一、exec() 简介:

二、QDialog::exec()

三、QCoreApplication::exec()

四、QEventLoop 的 exec()


一、exec() 简介:

1、说明:

事件循环,首先是一个无限 “循环”

程序在 exec() 里面无限循环,能让跟在 exec() 后面的代码得不到运行机会,直至程序从 exec() 跳出

其次,之所以被称为“事件”循环,是因为它能接收事件,并处理之。

当事件太多而不能马上处理完的时候,待处理事件被放在一个“队列”里,称为“事件循环队列”。

当事件循环处理完一个事件后,就从“事件循环队列”中取出下一个事件处理之。

当事件循环队列为空的时候,它和一个啥事也不做的永真循环有点类似,但是和永真循环不同的是,事件循环不会大量占用 CPU 资源。

事件循环的本质就是以队列的方式再次分配线程时间片

嵌套

一个线程 可拥有 多个 事件循环

但,事件循环必须是嵌套的,一层套一层

子层的事件循环执行 exec() 的时候,父层事件循环就处于中断状态;即:只有当前 QEventLoop 被激活

虽然父层中断,父层 GUI 界面处理不会卡住如下一段解释

当子层事件循环跳出 exec() 后,父层事件循环才能继续循环下去

划重点:

事件循环嵌套时,虽然父层循环处于中断状态,但:

子层事件循环具有父层事件循环的所有功能

所以当在主线程中启动各种 exec()(比如QEventLoop::exec())时,

虽然会打断 main 函数中的 QApplication::exec(),但是 Gui 界面还是可以正常响应,不会出现卡住的现象

这与用 while 来循环是不一样的。

2、作用

1. 进入时间循环;

2. wait,直到响应 a 可能的输入;

3. QT 接收和处理用户及系统交代的事件(消息),并传递到各个窗口;

4. 程序遇到 exit() 退出时,返回 exec() 的值。- 与 show() 的区别,show 直接返回

 

二、QDialog::exec()

子类 QDialog 调用 exec() 后,会新启一个消息循环:

QDialog 类的 exec() 函数里,同样调用了 show(),只是在后面又调用了一句 qApp->enter_loop() 嵌套一个新的消息循环来 阻塞当前事件 的执行;

然后在 hide() 函数里调用了qApp->exit_loop()来退出当前的消息循环并继续执行原事件。

 

enter_loop():

这个函数被废弃了。它仍然被保留下来是为了使旧的代码能够继续工作。

我们强烈建议不要在新写的代码里使用它。

这个函数直接介入主消息循环里(递归地)。

除非你真的知道你在做什么,否则不要调用它。

建议:QApplication::eventLoop()->enterLoop()。

exit_loop():

同样被废弃了。

建议使用:QApplication::eventLoop()->exitLoop()。

提醒:这两个操作都会进入主消息循环,慎用

源码分析:

int QDialog::exec()
{
...
    QEventLoop eventLoop;
    d->eventLoop = &eventLoop;
    QPointer<QDialog> guard = this;
    (void) eventLoop.exec(QEventLoop::DialogExec);
    if (guard.isNull())
        return QDialog::Rejected;
    d->eventLoop = 0;
...
}

可以看到,QDialog 的这种 exec() 其实内部也是最终产生了一个栈上的 QEventLoop 来进行事件循环

 

三、QCoreApplication::exec()

函数只有一个,只需调用一次,详情请看源码:

int QCoreApplication::exec()//函数定义
{
    // 检查 QCoreApplication 的对象是否存在,如果不存在则返回
    if (!QCoreApplicationPrivate::checkInstance("exec"))
        return -1;
    // QThreadData 是存储线程数据信息的一个类
    // 判断当前线程是否是主线程,如果不是则返回同时提醒用户
    QThreadData *threadData = self->d_func()->threadData;
    if (threadData != QThreadData::current()) {
        qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());
        return -1;
    }
    // 判断主事件循环是否已经开启了,如果已开启则返回
    if (!threadData->eventLoops.isEmpty()) {
        qWarning("QCoreApplication::exec: The event loop is already running");
        return -1;
    }
    threadData->quitNow = false;
    QEventLoop eventLoop;
    self->d_func()->in_exec = true;
    self->d_func()->aboutToQuitEmitted = false;
    // 调用eventLoop.exec()开启主事件循环,并进入阻塞状态等待返回
    int returnCode = eventLoop.exec();
    threadData->quitNow = false;
    if (self) {
        self->d_func()->in_exec = false;
        // 当主事件循环关闭并返回之后,则发送aboutToQuit信号
        if (!self->d_func()->aboutToQuitEmitted)
            emit self->aboutToQuit(QPrivateSignal());
        self->d_func()->aboutToQuitEmitted = true;
        sendPostedEvents(0, QEvent::DeferredDelete);
    }
    //exec()函数结束并返回从主事件循环那儿得到的返回值
    return returnCode;
}

最终调用 QEventLoop::exec() 形成事件循环

四、QEventLoop 的 exec()

内部正是在通过一个 while 循环去不断的 processEvents() 

int QEventLoop::exec(ProcessEventsFlags flags)
{
    //...
    while (!d->exit.loadAcquire())
        processEvents(flags | WaitForMoreEvents | EventLoopExec);
    //...
}
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
    if (!d->threadData->eventDispatcher)
        return false;
    if (flags & DeferredDeletion)
        QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
    return d->threadData->eventDispatcher->processEvents(flags);
}

可以很明显的看到,对于一个线程来说,无论其事件循环是内层嵌套还是在外层,其最终都会去调用

d->threadData->eventDispatcher

 

 

  • 9
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值