一、导读
在qt中,常见到三个exec,第一个是qapplication::exec(),第二个是qeventloop::exec,第三个是qthread::exec()。本文从源码角度来看看这三个exec()。
qapplication::exec()是qapplication类下的一个静态成员函数,该函数用于进入主事件循环。
qeventloop::exec是qeventloop类下的一个公共成员函数,用于进入主事件循环。
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: must be called from the main thread, self->metaobject()->classname()); return -1; } //检查是否存在事件循环,如果存在,则返回,否则继续后续操作。 if (!threaddata->eventloops.isempty()) { qwarning(qcoreapplication: 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: 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()实现中也调用到qeventloop的exec()。
四、qeventloop::exec()
qeventloop::exec()用于进入主事件循环并等待直到exit()被调用。在调用该函数时需要传入一个flags,如果指定了标志,则只处理标志允许的类型的事件,qt中支持以下几种标志:
序号 标志类型 描述
1 qeventloop::allevents 所有事件
2 qeventloop::excludeuserinputevents 不处理用户输入事件,例如buttonpress和keypress。
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(qobjectprivate::get(threaddata->thread.loadacquire()))->mutex); if (threaddata->quitnow) return -1; if (d->inexec) { qwarning(qeventloop: 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 exceptions from an event handler is not supported in qt. you must not let any exception whatsoever propagate through qt code. if that is not possible, in qt 5 you must at least reimplement qcoreapplication::notify() and catch all exceptions there.); } 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事件队列的接口,它从窗口系统和其他地方接收事件。然后它将这些事件发送到qcoreapplication或qapplication实例进行处理和交付。
高智能农药残留检测仪的用途说明
印刷电路板生产企业生益电子发布2022第一季度报告
直通HDC2022 | 润和软件一站式OpenHarmony服务全面使能行业智慧物联
高通欲组财团入股ARM,对芯片行业有何影响?
变频电源有必要加滤波器吗?电源滤波器对变频电源的作用?
Qt中的三个exec之间有什么联系
快速了解声网Agora SDK 3.0
ZUKZ2Pro、360N5s售价不足2000元,却有6+128GB大内存,三年不过时!
集成运放的主要参数是什么?
华为麒麟9000s是几纳米工艺
5G超级SIM卡魅力何在,迪信通力推承诺销售一千万张
利用外部SRAM和CPLD构成先进先出缓冲器FIFO的设计
关于商用和工业用发动机发展趋势分析
“会员配送费更贵”?终究是被割韭菜
基于BP算法的前馈神经网络
不同的操作系统访问服务器很麻烦?来看看虹科Linux远程桌面解决方案!
什么是基站?基站由哪些组成?你知道5G基站和4G基站有哪些区别吗?
新能源汽车充电桩费用开始上涨 充电桩价格上涨潮要来了吗
石英灯电子变压器原理及故障检修,Electronic Transformer
微控制器控制的电流环路AFE保护