理解QThread
理解和使用 QThread
QThread实现相关
Qt 4.3(包括)之前,QThread::run()是纯虚函数,必须子类化QThread并实现run()函数。官方例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class 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();
}QThread实例归属于初始化它的主线程(或者其他工作线程),而不是运行它的run()函数的新线程, 这意味着QThread的所有queued connections连接类型的槽函数和invoked methods都将在旧线程(QThread实例的归属线程)执行。因此,希望在新线程中调用槽函数的开发人员必须使用
Qt 4.4之后引入的worker-object方法; 不应将新插槽直接实现到QThread的子类中。与
queued connections连接类型的槽函数或invoked methods不同,直接调用QThread对象的方法将在调用该方法的线程(也就是QThread的归属线程)中执行。当子类化
QThread时,请记住构造函数在旧线程中执行,而run()在新线程中执行。 在QThread的成员函数和在run()函数中访问QThread的成员变量时,实际上是从两个不同的线程访问该变量。 必须检查同时访问的安全性,确认是否需要加互斥锁。注意:
跨线程与对象交互时必须小心。
作为一般规则,只能从创建
QThread对象本身的线程调用QThread的成员函数(例如 setPriority()),除非 文档另有说明。有关详细信息,请参阅同步线程。
从
Qt 4.4开始,QThread::run()默认调用QThread::exec()。这样就子类化
QThread就不是必须的了,只需要一个继承自QObject的子类实现线程函数,并将该子类实例使用moveToThread()移动到线程对象中,并将线程函数Connect到线程对象的operator ()就可以了。这是
Bradley T. Hughes推荐的方法。官方例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37class 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的槽函数连接到其他任意线程中的任意对象的随便什么信号上,跨线程连接信号和槽函数是安全的,由queued connections机制保证。
线程管理
QThread提供了两个信号started() 和 finished()用来通知用户线程已经启动或停止, 也可以用成员函数isFinished() 和 isRunning()主动查询线程的状态.
调用 exit() 或者 quit()可以停止线程,极端情况下还可以调用 terminate()直接终止正在运行的线程, 但是并不鼓励这么做。
Qt 4.8之后,通过连接QThread::finished()信号和QObject::deleteLater()槽函数,可以在线程结束的时候删除其中存活的对象。
wait() 阻塞调用线程,直到wait()的目标线程结束。
QThread 提供的sleep函数(静态、平台无关):sleep()、 msleep()、usleep() 。
备注: 因为Qt是事件驱动的,所以wait() 和 sleep() 函数不是必需的。监听finished()信号可以替代 wait(),sleep() 也可以用 QTimer替代。
QThread类静态函数 currentThreadId() 和currentThread()返回当前运行线程的标识符。 前者返回平台相关的线程ID; 后者返回QThread 对象指针。
例程
定义一个普通的QObject派生类,然后将其对象move到QThread中。
使用信号和槽时不用考虑多线程的存在。
也不用使用QMutex来进行同步,Qt的事件循环会自动处理好。
1 | // worker.h |
1 | // worker.cpp |
1 | // main.cpp |
1 | # CMakeLists.txt |
执行输出结果:
1 | #thread_id ------- function |