1. QT中的线程类QThread
头文件要求: #include
qmake: QT += core
继承自: QObject
一个QThread管理程序中的一个线程,QThread执行run()后就启动线程。默认情况下,run()通过执行exec()启动事件循环,并且在线程内运行QT事件循环。
2.QT中开启线程的两种方法
推荐使用moveToThread方法开启线程,不用考虑安全问题。
2.1 对于已存在的类对象的成员函数——使用QObject::moveToThread().
这种方法将一个现有的某对象的成员函数移动到新建线程中执行。为实现此目标,需要对现有类进行更改。
优势:既有类中的成员函数改为槽函数后可以和任意线程中的signal连接,由于queued connections机制,这种连接是安全的。
class Worker : public QObject
{
Q_OBJECT
// #1 既有类需要增加槽函数 doWork,并emit <signal>
public slots:
void doWork(const QString ¶meter) {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
// #2 既有类需要增加信号函数,信号函数不用实现,但是应该跟之前emit的信号相同。
signals:
void resultReady(const QString &result);
};
class Controller : public QObject
{
Q_OBJECT
// #3 新建线程类对象,用来接收既有类
QThread workerThread;
public:
Controller() {
// #4. 初始化既有类,通过moveToThread函数转移到之前新建的线程对象workerThread中
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
// #5.1 connect线程结束信号和销毁槽函数
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
// #5.2 connect当前类的按钮点击之类的操作信号和既有类的耗时函数
connect(this, &Controller::operate, worker, &Worker::doWork);
// #5.3 connect既有类的耗时函数相关的信号和当前类处理该信号的槽函数
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
// # 6. 启动线程,执行线程类的run()
workerThread.start();
}
~Controller() {
// #7. 在当前类的析构函数中退出线程
workerThread.quit();
workerThread.wait();
}
// #8.为当前类增加处理既有类信号的槽函数,该信号与线程doWork相关
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
2.2 使代码在单独的线程中运行的另一种方法是子类QThread并重新实现运行()。安全性较低。
这种方法中,线程将在返回 run 函数后退出。除非调用exec(),否则线程中不会运行任何事件循环。
注意:QThread 实例位于实例化它的旧线程中,而不是位于调用run() 的新线程中。这意味着 QThread 的所有排队槽和调用的方法都将在旧线程中执行。因此,希望在新线程中调用槽的开发人员必须使用工作对象方法;新槽不应直接实现到子类化的 QThread 中。
与queued connections或调用的方法不同,直接在 QThread 对象上调用的方法将在调用该方法的线程中执行。子类化 QThread 时,请记住,构造函数在旧线程中执行,而run() 在新线程中执行。如果从两个函数访问成员变量,则从两个不同的线程访问该变量。检查这样做是否安全。
注意:与跨不同线程的对象进行交互时必须小心。作为一般规则,只能从创建线程调用函数广告对象本身(例如setPriority()),除非文档另有说明。有关详细信息,请参阅同步线程。
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();
}