文章目录
- [一、 QCoreApplication的exec()实现](#一、 QCoreApplication的exec()实现)
- [二、 QEventLoop的exec()实现](#二、 QEventLoop的exec()实现)
-
- [1. D指针用法](#1. D指针用法)
- [2. 获取线程数据](#2. 获取线程数据)
- [3. 加锁和判断](#3. 加锁和判断)
- [4. 局部类`LoopReference`](#4. 局部类
LoopReference
) -
- [4.1 `LoopReference`的构造函数:](#4.1
LoopReference
的构造函数:) - [4.2 QThreadData的成员变量](#4.2 QThreadData的成员变量)
- [4.3 `LoopReference`的析构函数](#4.3
LoopReference
的析构函数) - [4.4 小结](#4.4 小结)
- [4.1 `LoopReference`的构造函数:](#4.1
- [5. 事件循环](#5. 事件循环)
-
- [5.1 processEvents()](#5.1 processEvents())
- 三、小结
源码版本:Qt 6.5.0
主程序中一般都少不了这两行代码:
cpp
QApplicaton app(argc, argv);
...
app.exec();
下面来跟踪一下它的实现。
一、 QCoreApplication的exec()实现
cpp
int QCoreApplication::exec()
{
if (!QCoreApplicationPrivate::checkInstance("exec"))
return -1;
QThreadData *threadData = self->d_func()->threadData.loadAcquire();
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;
int returnCode = eventLoop.exec(QEventLoop::ApplicationExec);
threadData->quitNow = false;
if (self)
self->d_func()->execCleanup();
return returnCode;
}
可以看到application的exec也是调用了QEventLoop
的exec。
这里不太重要,那么就继续看QEventLoop
的实现。
二、 QEventLoop的exec()实现
cpp
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<QMutex> &locker;
bool exceptionCaught;
LoopReference(QEventLoopPrivate *d, QMutexLocker<QMutex> &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.");
}
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);
// remove posted quit events when entering a new event loop
QCoreApplication *app = QCoreApplication::instance();
if (app && app->thread() == thread())
QCoreApplication::removePostedEvents(app, QEvent::Quit);
while (!d->exit.loadAcquire())
processEvents(flags | WaitForMoreEvents | EventLoopExec);
ref.exceptionCaught = false;
return d->returnCode.loadRelaxed();
}
后文将对QEventLoop::exec()
实现的每一行按顺序进行解读。
1. D指针用法
cpp
Q_D(QEventLoop)
Qt中非常常见的D指针用法(pimpl惯用法),用于在类Xxx
中访问隐藏部分类XxxPrivate
的实现。
宏展开就是:
cpp
QEventLoopPrivate* const d = d_func();
后续可以用变量d
来访问QEvnetLoop
的隐藏实现部分,也即在类QEvnetLoopPrivate
的部分。
相对应的还有Q指针用法
Q_Q
,用于在XxxPrivate
中访问Xxx
的实现。
2. 获取线程数据
auto threadData = d->threadData.loadRelaxed();
这里的d->threadData
定义在类QObjectPrivate
中,如下:
cpp
public:
mutable ExtraData *extraData; // extra data set by the user
// This atomic requires acquire/release semantics in a few places,
// e.g. QObject::moveToThread must synchronize with QCoreApplication::postEvent,
// because postEvent is thread-safe.
// However, most of the code paths involving QObject are only reentrant and
// not thread-safe, so synchronization should not be necessary there.
QAtomicPointer<QThreadData> threadData; // id of the thread that owns the object
翻译一下就是:
cpp
mutable ExtraData *extraData; // 用户设置的额外数据
// 此原子操作在某些地方需要获取/释放语义,
// 例如,QObject::moveToThread 必须与 QCoreApplication::postEvent 同步,
// 因为 postEvent 是线程安全的。
// 然而,涉及 QObject 的大多数代码路径只是可重入的并且不是线程安全的,
// 所以在这些地方不需要同步。
QAtomicPointer<QThreadData> threadData; // 拥有该对象的线程的 ID
类QObjectPrivate
继承自QObjectData
,如下:
cpp
class Q_CORE_EXPORT QObjectPrivate : public QObjectData
{
public:
Q_DECLARE_PUBLIC(QObject)
...
};
而在QObject
中也有一个指针成员变量存储了QObjectData
,如下:
cpp
...
protected:
QObject(QObjectPrivate &dd, QObject *parent = nullptr);
protected:
QScopedPointer<QObjectData> d_ptr;
...
并且在构造时将d_ptr
赋值为了QObjectPrivate
。
cpp
QObject::QObject(QObject *parent)
: QObject(*new QObjectPrivate, parent)
{
}
QObject::QObject(QObjectPrivate &dd, QObject *parent)
: d_ptr(&dd)
{
Q_ASSERT_X(this != parent, Q_FUNC_INFO, "Cannot parent a QObject to itself");
Q_D(QObject);
d_ptr->q_ptr = this;
auto threadData = (parent && !parent->thread()) ? parent->d_func()->threadData.loadRelaxed() : QThreadData::current();
threadData->ref();
...
}
总之明白一点:每个QObject
类都有线程数据,记录了它的线程信息,可以知道这个类属于哪一个线程。
至于其中的类(模板)QAtomicPointer
继承自类(模板)QBasicAtomicPointer
,QBasicAtomicPointer
组合了变量QAtomicOps
,QAtomicOps
相当于std::atomic
。
本质上是用std::atomic
存储了模板类型T
的指针,保证访问的原子性。
所以auto threadData = d->threadData.loadRelaxed();
这一行代码也仅仅是获取QObjectPrivate
中的线程数据,原子性地。
而且QEventLoop
也是继承自QObject
的,通过这一行代码,至少可以得知Qt中线程和事件循环的关系的一个结论:
每个事件循环都都自己所属的线程,拥有相关的线程数据。
在启动事件循环时首先会去获取线程数据。
3. 加锁和判断
cpp
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;
}
接下来,对线程数据进行了加锁。进行简单判断是否要退出、提前结束。
4. 局部类LoopReference
cpp
struct LoopReference {
QEventLoopPrivate *d;
QMutexLocker<QMutex> &locker;
bool exceptionCaught;
LoopReference(QEventLoopPrivate *d, QMutexLocker<QMutex> &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.");
}
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);
接下来定义了一个局部类LoopReference
,并创建了一个栈变量ref
。
4.1 LoopReference
的构造函数:
构造函数传入了一个事件循环(的隐藏实现部分,QEventLoopPrivate *
),通过d->inExec = true;
将事件循环标记为在执行中,然后将d->exit
赋值为false
。
然后将线程数据中的loopLevel
加1,大概是递增了循环层深,对应这两个代码:
cpp
auto threadData = d->threadData.loadRelaxed();
++threadData->loopLevel;
然后向线程数据threadData
中push了一个QEventLoop*
,这里我们需要看下QThreadData
的实现来了解这行push在干什么:
4.2 QThreadData的成员变量
cpp
class QThreadData
{
public:
QThreadData(int initialRefCount = 1);
...
public:
int loopLevel;
int scopeLevel;
QStack<QEventLoop *> eventLoops;
QPostEventList postEventList;
QAtomicPointer<QThread> thread;
QAtomicPointer<void> threadId;
QAtomicPointer<QAbstractEventDispatcher> eventDispatcher;
QList<void *> tls;
bool quitNow;
bool canWait;
bool isAdopted;
bool requiresCoreApplication;
};
原来QThreadData
中用栈存储了一系列的QEventLoop
,还有一个QPostEventList posrtEventList;
,这个大概是用来存放post的事件的。
事件的发送有send
和post
两种机制,send
的事件会立刻执行,post
的则需要放到队列中,靠事件循环去推动。所以并没有看到sendEventList
, 灰常合理。
同时里面还有:
- 线程
thread
- 线程id
threadId
- 事件分发器
eventDispatcher
(底层实现与平台相关)。 tls
???大概是用于Thread Local Storaged
的。
还有一些变量暂时没用到,简单看下即可,比如requiresCoreApplication
大概是用于判断是否需要QCoreApplication
。
4.3 LoopReference
的析构函数
cpp
~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.");
}
locker.relock();
auto threadData = d->threadData.loadRelaxed();
~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.");
}
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;
}
Q_ASSERT_X(eventLoop == d->q_func(), "QEventLoop::exec()", "internal error");
Q_UNUSED(eventLoop); // --release warning
d->inExec = false;
--threadData->loopLevel;
}
析构时将执行了逆操作:
QEventLoop *eventLoop = threadData->eventLoops.pop();
- 从线程数据中弹出一个时间循环。
d->inExec = false;
- 将该事件循环标记为未在执行。
--threadData->loopLevel;
- 递减事件循环层深。
4.4 小结
至此,局部类LoopReference
的作用就搞清楚了,
cpp
LoopReference ref(d, locker);
上面这行代码的作用就是按照RAII原则,
在构造时:
- 将传入的事件循环标记为在执行中。
- 将事件循环压放入线程数据(
threadData
)的事件循环栈中。 - 将线程数据(
threadData
)中的循环层深loopLevel
加1。
在析构时执行逆操作:
- 将传入的事件循环标记为未在执行。
- 从线程数据(
threadData
)的事件循环栈中弹出一个事件循环。 - 将线程数据(
threadData
)的循环层深loopLevel
减1。
至于这些改动/标记在何处被使用,暂时还不得而知。
5. 事件循环
cpp
// remove posted quit events when entering a new event loop
QCoreApplication *app = QCoreApplication::instance();
if (app && app->thread() == thread())
QCoreApplication::removePostedEvents(app, QEvent::Quit);
while (!d->exit.loadAcquire())
processEvents(flags | WaitForMoreEvents | EventLoopExec);
ref.exceptionCaught = false;
return d->returnCode.loadRelaxed();
剩下的代码只有这么多。
其中进行了判断:如果app的当前线程和该事件循环处于同一线程,就移除app中的Quit
事件。
QCoreApplication
也继承自QObject
,其中的QObjectPrivate
中的QThreadData
部分存有一个QList<QPostEvent> postEvetList
。
暂时想不通为什么会有、哪里来的Quit
事件,不过这两行代码本身较容易理解。
最后来到事件循环的核心部分:一个while
循环:
cpp
while (!d->exit.loadAcquire())
processEvents(flags | WaitForMoreEvents | EventLoopExec);
简单粗暴,直到d->exit
为true
时才会退出循环。否则就继续调用processEvents
,并传入flags。
循环退出后,将ref.exceptionCaught
赋为false
,没什么大作用,只会让LoopReference
析构时不会警告。
然后返回计数码d->returnCode
。
5.1 processEvents()
processEvents()
的实现如下:
cpp
bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
Q_D(QEventLoop);
auto threadData = d->threadData.loadRelaxed();
if (!threadData->hasEventDispatcher())
return false;
return threadData->eventDispatcher.loadRelaxed()->processEvents(flags);
}
可见其中也是调用了事件循环所属线程的eventDispatcher
的processEvents()
。
eventDispatcher
的类型如下:
QAtomicPointer<QAbstractEventDispatcher> eventDispatcher;
事件分发器的实现与平台相关,这一块的实现会再开一篇。
三、小结
-
Qt一般将类分为公开部分和隐藏部分,例如
QWidget
和QWidgetPrivate
。 -
Qt中宏
Q_D
用于在公开类访问隐藏实现部分(获取d
指针),Q_Q
用于在隐藏实现部分访问公开部分(获取q
指针)。 -
每个
QObject
背后都存有一个QObjectData
指针,在构造时用QObjectData *
为其赋值;QObjectPrivate
继承自QObejctData
,其中存有成员变量QThreadData
,记录了线程数据。 -
事件循环
QEventLoop
继承自QObject
,所以每个事件循环都有所属线程。(QCoreApplication
和其他类同理) -
线程数据
QThreadData
中存有以下重要变量:cppint loopLevel; // 事件循环层深 int scopeLevel; QStack<QEventLoop *> eventLoops; //每个线程对应多个事件循环 QPostEventList postEventList; // 线程的事件队列 QAtomicPointer<QThread> thread; // 线程 QAtomicPointer<void> threadId; // 线程ID QAtomicPointer<QAbstractEventDispatcher> eventDispatcher; //事件分发器 QList<void *> tls; //Thread Local Storage