龙空技术网

Qt中这三个exec,还傻傻分不清吗?

嵌入式小生 260

前言:

此刻同学们对“javaexec”大体比较注意,朋友们都想要了解一些“javaexec”的相关资讯。那么小编同时在网上汇集了一些有关“javaexec””的相关知识,希望兄弟们能喜欢,大家一起来了解一下吧!

一、导读

在Qt中,常见到三个exec,第一个是QApplication::exec(),第二个是QEventLoop::exec,第三个是QThread::exec()。本文从源码角度来看看这三个exec()

QApplication::exec()QApplication类下的一个静态成员函数,该函数用于进入主事件循环。QEventLoop::execQEventLoop类下的一个公共成员函数,用于进入主事件循环。QThread::exec()QThread类下的一个受保护的成员函数,也是用于进入事件循环。

都是进入事件循环,他们之间有什么联系呢,接着后面的分析。

二、QApplication::exec()

在实际开发中,必须调用QApplication::exec()来启动事件处理,主事件循环会从窗口系统接收事件,并将这些事件分派给应用程序小部件。在调用exec()之前不能发生任何用户交互,但是存在一种特殊的情况:QMessageBox这样的模态小部件可以在调用exec()之前使用,因为模态小部件会调用exec()来启动本地事件循环。

从源码角度,QApplication::exec()会调用QGuiApplication::exec()QGuiApplication::exec()会调用QCoreApplication::exec()

int QCoreApplication::exec(){    //检查exec实例    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事件循环对象    QEventLoop eventLoop;    self->d_func()->in_exec = true;    self->d_func()->aboutToQuitEmitted = false;    //启动事件循环    int returnCode = eventLoop.exec();    threadData->quitNow = false;    if (self)        self->d_func()->execCleanup();    return returnCode;}

从上述源码可见,QApplication的exec()经过层层调用,最终是使用QEventLoop实现事件循环。

QApplication::exec()用于启动应用程序的事件循环,让应用程序能得以启动运行并接收事件。

『备注,执行应用清理的优雅方式』:

建议将清理代码连接到aboutToQuit()信号,而不是放在应用程序的main()函数中。这是因为,在某些平台上,QApplication::exec()调用可能不会返回。例如,在Windows平台上,当用户注销时,系统会在Qt关闭所有顶级窗口后终止该进程。因此,不能保证应用程序有足够的时间退出事件循环,并在QApplication::exec()调用之后,即在main()函数的末尾执行代码。

QCoreApplication::exec()函数实现中的这几行代码:

    if (threadData != QThreadData::current()) {        qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());        return -1;    }

引发出另一个有趣的知识点,那就是:在Qt多线程开发中,需要注意不要阻塞GUI线程,那么哪个是GUI线程呢?从上述源码可以明确知道:QApplication a(argc, argv);所在线程就是GUI线程。

三、QThread::exec()

在多线程应用设计中,QThread::exec()用于为当前线程启动一个新的事件循环,为存在于该线程中的对象交付事件。在源码中,QThread::exec()实现如下:

int QThread::exec(){    Q_D(QThread);    QMutexLocker locker(&d->mutex);    d->data->quitNow = false;    if (d->exited) {        d->exited = false;        return d->returnCode;    }    locker.unlock();    //创建QEventLoop事件循环。    QEventLoop eventLoop;    int returnCode = eventLoop.exec();    locker.relock();    d->exited = false;    d->returnCode = -1;    return returnCode;}

从源码角度,也可见QThread::exec()实现中也调用到QEventLoopexec()

四、QEventLoop::exec()

QEventLoop::exec()用于进入主事件循环并等待直到exit()被调用。在调用该函数时需要传入一个flags,如果指定了标志,则只处理标志允许的类型的事件,Qt中支持以下几种标志:

序号

标志类型

描述

1

QEventLoop::AllEvents

所有事件

2

QEventLoop::ExcludeUserInputEvents

不处理用户输入事件,例如ButtonPressKeyPress

3

QEventLoop::ExcludeSocketNotifiers

不要处理套接字通知事件。

4

QEventLoop::WaitForMoreEvents

如果没有可用的挂起事件,则等待事件。

注,没有被传递的事件不会被丢弃,这些事件将在下次传入不过滤事件的标志调用procesvents()时被传递。

从源码角度,QEventLoop::exec()实现如下:

int QEventLoop::exec(ProcessEventsFlags flags){    Q_D(QEventLoop);    auto threadData = d->threadData.loadRelaxed();    //we need to protect from race condition with QThread::exit    QMutexLocker locker(&static_cast<QThreadPrivate *>(QObjectPrivate::get(threadData->thread.loadAcquire()))->mutex);    if (threadData->quitNow)        return -1;    if (d->inExec) {        qWarning("QEventLoop::exec: instance %p has already called exec()", this);        return -1;    }    struct LoopReference {        QEventLoopPrivate *d;        QMutexLocker &locker;        bool exceptionCaught;        LoopReference(QEventLoopPrivate *d, QMutexLocker &locker) : d(d), locker(locker), exceptionCaught(true)        {            d->inExec = true;            d->exit.storeRelease(false);            auto threadData = d->threadData.loadRelaxed();            ++threadData->loopLevel;            threadData->eventLoops.push(d->q_func());            locker.unlock();        }        ~LoopReference()        {            if (exceptionCaught) {                qWarning("Qt has caught an exception thrown from an event handler. Throwing\n"                         "exceptions from an event handler is not supported in Qt.\n"                         "You must not let any exception whatsoever propagate through Qt code.\n"                         "If that is not possible, in Qt 5 you must at least reimplement\n"                         "QCoreApplication::notify() and catch all exceptions there.\n");            }            locker.relock();            auto threadData = d->threadData.loadRelaxed();            QEventLoop *eventLoop = threadData->eventLoops.pop();            Q_ASSERT_X(eventLoop == d->q_func(), "QEventLoop::exec()", "internal error");            Q_UNUSED(eventLoop); // --release warning            d->inExec = false;            --threadData->loopLevel;        }    };    LoopReference ref(d, locker);    // 当进入一个新的事件循环时,删除已发布的exit事件    QCoreApplication *app = QCoreApplication::instance();    if (app && app->thread() == thread())        QCoreApplication::removePostedEvents(app, QEvent::Quit);#ifdef Q_OS_WASM    // Partial support for nested event loops: Make the runtime throw a JavaSrcript    // exception, which returns control to the browser while preserving the C++ stack.    // Event processing then continues as normal. The sleep call below never returns.    // QTBUG-70185    if (threadData->loopLevel > 1)        emscripten_sleep(1);#endif    while (!d->exit.loadAcquire())        processEvents(flags | WaitForMoreEvents | EventLoopExec);    ref.exceptionCaught = false;    return d->returnCode.loadRelaxed();}

从上述源码可知,QEventLoop::exec()本质会调用processEvents()分发事件。processEvents()实现如下:

从上图所示代码中,会调用QAbstractEventDispatcher::processEvents()实现事件分发。QAbstractEventDispatcher类提供了一个管理Qt事件队列的接口,它从窗口系统和其他地方接收事件。然后它将这些事件发送到QCoreApplicationQApplication实例进行处理和交付。

标签: #javaexec #c语言 exec #java执行exec