QT学习(4):QThread
目录
QT官方文档
QThread 对象管理程序中的一个控制线程。QThreads 在run()
中开始执行。默认情况下,run()
通过调用exec()
来启动事件循环,并在线程内运行 Qt 事件循环。
1、使用moveToThread()
可以通过使用moveToThread()
将工作器对象移动到线程来使用它们。
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString ¶meter) {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() {
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
然后,Worker 槽函数中的代码将在单独的线程中执行。但是,可以自由地将 Worker 的槽函数连接到任何线程中来自任何对象的任何信号。跨不同线程连接信号和槽函数是安全的,这要归功于一种称为队列连接的机制。
2、子类化 QThread
使代码在单独的线程中运行的另一种方法是子类化 QThread 并重新实现run()
。
class WorkerThread : public QThread
{
Q_OBJECT
void run() override {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &s);
};
void MyObject::startWorkInAThread()
{
WorkerThread *workerThread = new WorkerThread(this);
connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
workerThread->start();
}
在该示例中,线程将在 run 函数返回后退出。除非调用exec()
,否则线程中不会运行任何事件循环。
重要的是要记住,QThread 实例所在线程是实例化它的旧线程,而不是调用run()
的新线程。这意味着所有 QThread 的队列槽函数或invoked的方法都将在旧线程中执行。因此,希望在新线程中调用槽的开发人员必须使用worker-object方法,新槽函数不应直接定义在 QThread子类对象中。
与队列槽函数或invoked的方法不同,直接在 QThread 对象上调用的方法将在调用该方法的线程中执行。在对 QThread 进行子类化时,请记住,构造函数在旧线程中执行,而run()
在新线程中执行。如果从两个函数访问成员变量,则从两个不同的线程访问该变量。需要保证线程安全。
3、管理线程
当线程为started()和finished()
时,QThread 会通过信号通知,或者可以使用isFinished()和isRunning()
来查询线程的状态。
您可以通过调用exit()或quit()
来停止线程。在极端情况下,您可能希望使用terminate()
强制终止一个执行线程。然而,这样做是危险和不鼓励的。
从 Qt 4.8开始,可以通过将finished()
信号连接到QObject::deleteLater()
来释放存在于刚刚结束的线程中的对象。
使用wait()
阻塞调用线程,直到另一个线程完成执行(或直到经过指定的时间)。
QThread 还提供静态的、独立于平台的休眠功能:sleep(),msleep(),usleep()
分别允许全秒、毫秒和微秒。
注意:wait()和sleep()
函数通常应该是不必要的,因为 Qt 是一个事件驱动的框架。请考虑监听finished()信号,而不是wait()。请考虑使用QTimer函数,而不是sleep()函数。
静态函数currentThreadId()、currentThread()
返回当前正在执行的线程的标识符。前者返回线程的特定于平台的 ID,后者返回一个 QThread 指针。
信号槽和线程
QObject::connect()的最后一个参数将指定连接类型:
- Qt::DirectConnection:直接连接意味着
槽函数将在信号发出的线程直接调用
- Qt::QueuedConnection:队列连接意味着向接受者所在线程发送一个事件,该线程的事件循环将获得这个事件,然后之后的某个时刻调用槽函数
- Qt::BlockingQueuedConnection:阻塞的队列连接就像队列连接,但是发送者线程将会阻塞,直到接受者所在线程的事件循环获得这个事件,槽函数被调用之后,函数才会返回
- Qt::AutoConnection:自动连接(默认)意味着如果接受者所在线程就是当前线程,则使用直接连接;否则将使用队列连接
- Qt::UniqueConnection:唯一连接,对于一对信号槽,执行多次相应的connect函数,只会连接一次,在connect中判断如果是唯一连接且已经连接会直接返回;否则连接多次,发出一次信号,调用多次槽函数。
注意在上面每种情况中,发送者所在线程都是无关紧要的!在自动连接情况下,Qt 需要查看信号发出的线程是不是与接受者所在线程一致,来决定连接类型。注意,Qt 检查的是信号发出的线程,而不是信号发出的对象所在的线程!
在程序中,一般将处理任务的部分与管理线程的部分分离。简单来说,我们可以像QT官方文档中记录的第一种方法一样,利用一个QObject的子类,使用QObject::moveToThread()
改变其线程依附性,而不是继承QThread类,在类中重写run函数。
主线程即main函数的线程,即GUI线程,QCoreApplication创建了代表主线程的QThread对象。
不能有两个线程同时访问一个QObject对象,除非这个对象的内部数据都已经很好地序列化(例如为每个数据访问加锁)。记住,在你从另外的线程访问一个对象时,它可能正在处理所在线程的事件循环派发的事件!基于同样的原因,你也不能在另外的线程直接delete一个QObject对象,相反,你需要调用QObject::deleteLater()
函数,这个函数会给对象所在线程发送一个删除的事件。
QThread源码
QThread继承自QObject,主要的成员包括:
- currentThreadId()、currentThread()、sleep()等静态函数;
- started()和finished()两个信号;
- exit()、strat()、terminate()、quit()等控制线程的函数;
- run()和exec()两个受保护的函数。
class Q_CORE_EXPORT QThread : public QObject
{
Q_OBJECT
public:
static Qt::HANDLE currentThreadId() noexcept Q_DECL_PURE_FUNCTION;
static QThread *currentThread();
static int idealThreadCount() noexcept;
static void yieldCurrentThread();
explicit QThread(QObject *parent = nullptr);
~QThread();
enum Priority {//................};
void setPriority(Priority priority);Priority priority() const;
void requestInterruption(); bool isInterruptionRequested() const;
void setStackSize(uint stackSize);uint stackSize() const;
QAbstractEventDispatcher *eventDispatcher() const;
void setEventDispatcher(QAbstractEventDispatcher *eventDispatcher);
bool event(QEvent *event) override;int loopLevel() const;
bool isFinished() const;bool isRunning() const;
void exit(int retcode = 0);
public Q_SLOTS:
void start(Priority = InheritPriority);void terminate();void quit();
public:
bool wait(unsigned long time = ULONG_MAX);
static void sleep(unsigned long);static void msleep(unsigned long);static void usleep(unsigned long);
Q_SIGNALS:
void started(QPrivateSignal);void finished(QPrivateSignal);
protected:
virtual void run();int exec();
static void setTerminationEnabled(bool enabled = true);
protected:
QThread(QThreadPrivate &dd, QObject *parent = nullptr);
private:
Q_DECLARE_PRIVATE(QThread)
friend class QCoreApplication;
friend class QThreadData;
};
1、start方法:
在qthread_win.cpp中,可以看到start()
的定义。
- 在
d->isInFinish
为真时,对私有类中的互斥量d->mutex进行解锁、等待和重新加锁的操作。 - 如果线程已经处于运行状态,直接返回,否则对d指针的标志位进行初始化。
- 调用系统api创建线程,赋值给d指针的handle对象。
- 配置调度优先级(
winAPI:SetThreadPriority
)。 - 恢复线程运行(
winAPI:ResumeThread
)。
void QThread::start(Priority priority)
{
Q_D(QThread);
QMutexLocker locker(&d->mutex);
//在 d->isInFinish 为真时,对 d->mutex 进行解锁、等待和重新加锁的操作。
if (d->isInFinish) {
locker.unlock();
wait();
locker.relock();
}
if (d->running)
return;
//d指针的数据初始化
d->running = true;
d->finished = false;
d->exited = false;
d->returnCode = 0;
d->interruptionRequested = false;
//调用系统api创建线程,赋值给d指针的handle对象
#if defined(Q_CC_MSVC) && !defined(_DLL)
d->handle = (Qt::HANDLE) _beginthreadex(NULL, d->stackSize, QThreadPrivate::start,
this, CREATE_SUSPENDED, &(d->id));
#else
// MSVC -MD or -MDd or MinGW build
d->handle = CreateThread(nullptr, d->stackSize,
reinterpret_cast<LPTHREAD_START_ROUTINE>(QThreadPrivate::start),
this, CREATE_SUSPENDED, reinterpret_cast<LPDWORD>(&d->id));
#endif // Q_OS_WINRT
if (!d->handle) {
qErrnoWarning("QThread::start: Failed to create thread");
d->running = false;
d->finished = true;
return;
}
int prio;
d->priority = priority;
switch (d->priority) {
//********省略****************给prio赋值
}
//调度级别配置
if (!SetThreadPriority(d->handle, prio)) {
qErrnoWarning("QThread::start: Failed to set thread priority");
}
//创建时处于挂起状态,需要恢复线程执行
if (ResumeThread(d->handle) == (DWORD) -1) {
qErrnoWarning("QThread::start: Failed to resume new thread");
}
}
在创建线程的函数CreateThread中,调用了QThreadPrivate::start()
,在QThreadPrivate::start()
中调用了QThread::run()
函数。
unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(void *arg) noexcept
{ QThread *thr = reinterpret_cast<QThread *>(arg);
//...............
emit thr->started(QThread::QPrivateSignal());
thr->run();
}
2、run方法
调用run()
后进入所创建的子线程,每一个QThread都会默认开启一个事件循环,且只作用于当前所处的线程内
void QThread::run()
{
(void) exec();
}
QThread::exec()
方法是一个阻塞调用,它会一直阻塞当前线程,直到事件循环终止。因此,在调用 exec()
之后的代码将无法执行,直到事件循环退出。
该函数的主要功能和QCoreApplication::exec()
一样,是创建一个事件循环,并执行eventLoop.exec()
开启事件循环。只不过前者是子线程中的事件循环,后者是主线程即UI线程的事件循环。在QEventLoop ::exec()
中调用QEventLoop ::processEvents()
检查事件调度器(在QThreadPrivate::start
里创建)是否存在,若存在则通过QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
处理该线程内的事件。
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 eventLoop;
int returnCode = eventLoop.exec();
locker.relock();
d->exited = false;
d->returnCode = -1;
return returnCode;
}
事件循环可以嵌套,在exec()函数中创建事件循环后,可以通过实例化QEventLoop对象创建子事件循环(QMessageBox::exec()、QDialog::exec()
也会新建子事件循环并启动),会中断父事件循环的exec()
,但是不会中断界面响应,因为父循环的大部分事件也会包含在子循环中。
例如使用QNetworkAccessManager
提交网络请求时,由于Qt的网络操作类是异步、非阻塞的,但是又需要在当前函数中直接获得返回值时,则可以使用QEventLoop阻塞运行,可以添加定时器实现超时。
QEventLoop eventLoop;
connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
eventLoop.exec();
QByteArray replyData = reply->readAll();
3、quit()方法
告知线程的事件循环退出。等效于调用 QThread::exit(0)
。
void QThread::quit()
{ exit(); }
4、exit()方法
停止的是事件处理。
在这个线程中不再启动 QEventLoop
void QThread::exit(int returnCode)
{
Q_D(QThread);
d->data->quitNow = true;
//对事件循环器遍历进行逐一清理
for (int i = 0; i < d->data->eventLoops.size(); ++i) {
QEventLoop *eventLoop = d->data->eventLoops.at(i);
eventLoop->exit(returnCode);
}
}
5、terminate()方法
调用winAPI:TerminateThread
。此功能很危险,不鼓励使用。线程可以在其代码路径中的任何点终止。线程可以在修改数据时终止。线程没有机会自行清理、解锁任何持有的互斥锁等。简而言之,仅在绝对必要时使用此功能。
void QThread::terminate()
{
Q_D(QThread);
QMutexLocker locker(&d->mutex);
if (!d->running)
return;
if (!d->terminationEnabled) {
d->terminatePending = true;
return;
}
#ifndef Q_OS_WINRT
TerminateThread(d->handle, 0);
#endif
QThreadPrivate::finish(this, false);//只发送finnish信号
}
QThreadPrivate源码
主要成员包括一个互斥量,一个QThreadData对象,running、finished、exited
等标志位。
和QT中其它类和私有类的关系类似,QThread主要提供了线程管理的接口,在接口函数中主要完成了两个工作:
- 创建并启动事件循环器;
- 改变私有类的标志位,并调用私有类的同名函数和QThreadData成员进行深层次的执行操作。
私有类创建事件调度器,在QThreadData中启动。
class QThreadPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QThread)
public:
QThreadPrivate(QThreadData *d = nullptr);
~QThreadPrivate();
void setPriority(QThread::Priority prio);
mutable QMutex mutex;
QAtomicInt quitLockRef;
bool running;
bool finished;
bool isInFinish; //when in QThreadPrivate::finish
std::atomic<bool> interruptionRequested;
bool exited;
int returnCode;
uint stackSize;
QThread::Priority priority;
static QThread *threadForId(int id);
static unsigned int __stdcall start(void *) noexcept;
static void finish(void *, bool lockAnyway=true) noexcept;
Qt::HANDLE handle;
unsigned int id;
int waiters;
bool terminationEnabled, terminatePending;
QThreadData *data;
static QAbstractEventDispatcher *createEventDispatcher(QThreadData *data);
void ref(){quitLockRef.ref();}
void deref(){//....... }
};
1、createEventDispatcher创建事件调度器
新建一个事件调度器,在QThreadData的同名函数中启动。
QAbstractEventDispatcher *QThreadPrivate::createEventDispatcher(QThreadData *data)
{
Q_UNUSED(data);
return new QEventDispatcherWin32;
}
2、QThreadPrivate::start
- 分配线程本地存储 (TLS) 索引
- 创建事件调度器并启动
- 发射
started()
信号 run()
函数创建事件循环器,启动线程运行
unsigned int __stdcall QT_ENSURE_STACK_ALIGNED_FOR_SSE QThreadPrivate::start(void *arg) noexcept
{
QThread *thr = reinterpret_cast<QThread *>(arg);
QThreadData *data = QThreadData::get2(thr);
qt_create_tls();
TlsSetValue(qt_current_thread_data_tls_index, data);
data->threadId.storeRelaxed(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));
QThread::setTerminationEnabled(false);
{
QMutexLocker locker(&thr->d_func()->mutex);
data->quitNow = thr->d_func()->exited;
}
data->ensureEventDispatcher();
emit thr->started(QThread::QPrivateSignal());
QThread::setTerminationEnabled(true);
thr->run();
finish(arg);
return 0;
}
3、QThreadPrivate::finish
- 发射finish信号
- 释放线程本地存储
- 删除事件调度器
- 调用
winAPI:CloseHandle
关闭线程
void QThreadPrivate::finish(void *arg, bool lockAnyway) noexcept
{
QThread *thr = reinterpret_cast<QThread *>(arg);
QThreadPrivate *d = thr->d_func();
QMutexLocker locker(lockAnyway ? &d->mutex : 0);
d->isInFinish = true;
d->priority = QThread::InheritPriority;
void **tls_data = reinterpret_cast<void **>(&d->data->tls);
locker.unlock();
emit thr->finished(QThread::QPrivateSignal());
QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
QThreadStorageData::finish(tls_data);
locker.relock();
QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher.loadRelaxed();
if (eventDispatcher) {
d->data->eventDispatcher = 0;
locker.unlock();
eventDispatcher->closingDown();
delete eventDispatcher;
locker.relock();
}
d->running = false;
d->finished = true;
d->isInFinish = false;
d->interruptionRequested = false;
if (!d->waiters) {
CloseHandle(d->handle);
d->handle = 0;
}
d->id = 0;
}
QThreadDate源码
主要成员包括事件循环栈、post事件队列、QThread对象、事件调度器和quitNow、canWait等标志位。
每个线程的主要数据都保存在该类中,通过访问该类来操作线程相关数据。
class QThreadData
{
public:
QThreadData(int initialRefCount = 1);
~QThreadData();
static Q_AUTOTEST_EXPORT QThreadData *current(bool createIfNecessary = true);
static void clearCurrentThreadData();
static QThreadData *get2(QThread *thread)
{ Q_ASSERT_X(thread != nullptr, "QThread", "internal error"); return thread->d_func()->data; }
void ref();void deref();
inline bool hasEventDispatcher() const{ return eventDispatcher.loadRelaxed() != nullptr; }
QAbstractEventDispatcher *createEventDispatcher();
QAbstractEventDispatcher *ensureEventDispatcher()
{
QAbstractEventDispatcher *ed = eventDispatcher.loadRelaxed();
if (Q_LIKELY(ed))
return ed;
return createEventDispatcher();
}
bool canWaitLocked(){//....}
// This class provides per-thread (by way of being a QThreadData member) storage for qFlagLocation()
class FlaggedDebugSignatures {//...};
private:
QAtomicInt _ref;
public:
int loopLevel;
int scopeLevel;
QStack<QEventLoop *> eventLoops;
QPostEventList postEventList;
QAtomicPointer<QThread> thread;
QAtomicPointer<void> threadId;
QAtomicPointer<QAbstractEventDispatcher> eventDispatcher;
QVector<void *> tls;
FlaggedDebugSignatures flaggedSignatures;
bool quitNow;
bool canWait;
bool isAdopted;
bool requiresCoreApplication;
};
创建事件调度器函数,调用线程私有类的同名函数进行创建,然后启动创建好的事件调度器。
QAbstractEventDispatcher *QThreadData::createEventDispatcher()
{
QAbstractEventDispatcher *ed = QThreadPrivate::createEventDispatcher(this);
eventDispatcher.storeRelease(ed);
ed->startingUp();
return ed;
}
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!