在项目中,一个QThread对象管理了一个线程。一般来说,线程的执行从run()函数开始。在Qt中,有两种方式,第一个种是继承QThread重写run()函数,第二种是继承QObject函数添加耗时操作,然后在调用QObject::moveToThread()函数。Qt中现在比较推荐使用moveToThread()。
当线程开始或结束的时候,QThread会通过started()和finished()信号来通知,或者你也可以使用isRunning()和isFinished()来查询线程的状态。你也可以调用exit()或quit()来停止线程。在极端情况下,你可能使用terminate()来强制终止一个正在执行的线程。
QObject::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一定要继承自QObject,否则调用就在同一个线程中,可以使用QThread::currentThreadId()来判断是否在哪个线程中。该调用方式中,工作线程槽中的代码会在单独的线程中执行。同时,你可以自由的将工作者线程中的槽或信号连接到任何对象的任何信号。因为队列连接的机制,该跨不同线程连接信号和槽的方式是安全的。
另一种使用线程的方式是继承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中的队列里槽和信号的方法都将在旧线程中执行。如果你想在新线程中调用信号和槽,新的信号和槽旧不能直接在子类化的QThread中实现。即在子类化QThread时,需要记住构造函数在旧线程中执行,run()在新线程中执行。如果一个变量是从这种两个函数中访问,那么这个变量就是从两个不同的线程访问,需要检查是否是线程安全(加锁)。