本文仅关注connect第五个参数中Qt::QueuedConnection与Qt::DirectConnection在多线程中的使用方法与差别。
所有5个参数的定义如下:
enum ConnectionType {
AutoConnection,
DirectConnection,
QueuedConnection,
BlockingQueuedConnection,
UniqueConnection = 0x80
};
Qt::QueuedConnection与Qt::DirectConnection的简介:
Qt::QueuedConnection:当控制返回到接收者线程的事件循环时,将调用该槽函数。槽函数在接收者的线程中执行。
Qt::DirectConnection:信号发出时将立即调用该槽函数。槽函数在信号发送者所在的线程中执行。
这里重点关注下我加粗的部分。
多线程事件循环中使用Qt::QueuedConnection的执行流程如下:
thread: 2中出发的信号被投递到了thread: 1的事件循环中;当thread: 1中的SignnalEvent被处理时就会执行对应的槽函数,槽函数在接收者的线程中执行。
多线程事件循环中使用Qt::DirectConnection的执行流程如下:
thread: 1中出发的信号直接调用了槽函数,不论槽函数是属于哪个对象的,槽函数在信号发送者所在的线程中执行。
实现方式1(继承QThread)
下面的代码会有3部分组成:worker_thread.h、processer.h和main.cpp。
WorkerThread类继承了QThread类并重写了run()方法,在run()方法中创建了定时器并启动当前线程的事件循环,QTimer::timeout信号以Qt::DirectConnection的方式关联了WorkerThread::onTimeout()槽函数,由于QTimer对象在run()方法中被创建并关联的槽函数并使用了Qt::DirectConnection参数,所以当QTimer::timeout触发时WorkerThread::onTimeout()的所在线程与run()所在线程是同一个。在WorkerThread::onTimeout()只做一件事,就是触发WorkerThread::timeout信号,该WorkerThread::timeout信号的所在线程与run()的也相同。
#ifndef WORKER_THREAD_H
#define WORKER_THREAD_H
#include <QThread>
#include <QTimer>
#include <QDebug>
#include <QObject>
/**
* @brief The WorkerThread class
*
*/
class WorkerThread: public QThread {
Q_OBJECT
public:
WorkerThread(QObject *parent=nullptr): QThread(parent) {
}
protected:
void run() override {
qDebug() << "WorkerThread thread id: " << QThread::currentThreadId();
m_timer = new QTimer();
connect(m_timer, &QTimer::timeout, this, &WorkerThread::onTimeout, Qt::DirectConnection);
m_timer->start(1000);
exec();
}
signals:
void timeout();
private slots:
void onTimeout() {
emit timeout();
}
private:
QTimer *m_timer;
};
#endif // WORKER_THREAD_H
Processer类就干一件事情,就是提供了一个公有的void onTimeout()槽函数,在这个槽函数中打印出线程id,用于确定当前的槽函数在哪个线程中执行。
#ifndef PROCESSER_H
#define PROCESSER_H
#include <QObject>
#include <QThread>
#include <QDebug>
class Processer: public QObject {
Q_OBJECT
public:
Processer(QObject *parent=nullptr): QObject(parent) {
qDebug() << "Processer thread id: " << QThread::currentThreadId();
}
public slots:
void onTimeout() {
qDebug() << "Processer slot thread id: " << QThread::currentThreadId();
}
};
#endif // PROCESSER_H
在main函数中将WorkerThread::timeout的信号与Processer::onTimeout槽进行关联
采用Qt::QueuedConnection关联
#include <QCoreApplication>
#include "worker_thread.h"
#include "Processer.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "Main thread id: " << QThread::currentThreadId();
WorkerThread *workerThread = new WorkerThread;
Processer *processer = new Processer;
QObject::connect(workerThread, &WorkerThread::timeout, processer, &Processer::onTimeout, Qt::QueuedConnection);
workerThread->start();
return a.exec();
}
可以看到Processser::onTimeout的线程id是与主线程(执行main函数的线程)相同而与WorkerThread::run()的线程id不同,证明事件被投递到主线程的事件循环中被处理了。
采用Qt::DirectConnection关联
#include <QCoreApplication>
#include "worker_thread.h"
#include "Processer.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "Main thread id: " << QThread::currentThreadId();
WorkerThread *workerThread = new WorkerThread;
Processer *processer = new Processer;
QObject::connect(workerThread, &WorkerThread::timeout, processer, &Processer::onTimeout, Qt::DirectConnection);
workerThread->start();
return a.exec();
}
可以看到Processser::onTimeout的线程id与WorkerThread::run()中输出的线程id相同而与主线程id不同,证明槽函数直接被WorkerThread::run()的线程调用了。
关注点
QTimer对象的创建时机QTimer对象的timeout事件与WorkerThread的关联方式为什么用Qt::DirectConnection而不是Qt::QueuedConnection- 在线程函数
run()中运行exec()后会发生什么
实现方式2(QObject::moveToThread)
Processer类的代码和实现方式1的相同,下面主要看下main函数:
Qt::QueuedConnection方式:
#include <QCoreApplication>
#include <QThread>
#include <QTimer>
#include "processer.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "Main thread id: " << QThread::currentThreadId();
QTimer *timer = new QTimer();
timer->start(1000);
Processer *processer = new Processer();
QObject::connect(timer, &QTimer::timeout, processer, &Processer::onTimeout, Qt::QueuedConnection);
QThread *thread = new QThread();
timer->moveToThread(thread);
thread->start();
return a.exec();
}
可以看到槽函数的线程id和主函数是相同的。
Qt::DirectConnection方式:
#include <QCoreApplication>
#include <QThread>
#include <QTimer>
#include "processer.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "Main thread id: " << QThread::currentThreadId();
QTimer *timer = new QTimer();
timer->start(1000);
Processer *processer = new Processer();
QObject::connect(timer, &QTimer::timeout, processer, &Processer::onTimeout, Qt::DirectConnection);
QThread *thread = new QThread();
timer->moveToThread(thread);
thread->start();
return a.exec();
}
可以看到槽函数的线程id是一个新的线程id,与主线程id是不同的。
总结
目前官方更推荐使用QObject::moveToThread的方式使用多线程。