QT知识点总结——QThread
QThread类提供了一个与平台无关的方式来管理线程。
一个QThread对象管理程序里的一个线程。当QThread对象执行run()后它管理的线程开始运行。默认情况下,run()函数通过调用
exec()函数启动该线程的事件循环。
QThread有两种用法:
- 使用QObject::moveToThread()将一个工作对象加入到某个线程中。
- 在QThread子类中重新实现run()函数。
QThread用法一:使用QObject::moveToThread()将一个工作对象加入到某个线程中
你可以通过使用QObject::moveToThread()来将一个工作对象加入到某个线程中。
代码实例:
// QThread用法一:使用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对象的槽函数代码将在一个单独的线程中执行。然而,在任何线程内,你可以自由的地将Worker的槽函数连接来自任何对象的任何信号。跨线程的信号和槽函数连接是安全的,这归功于一种称为排队连接的实现机制。
QThread用法二:在QThread子类中重新实现run()函数
另外一种让代码在独立的线程中运行的方法,是在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的所有队列插槽都将在旧线程中执行。因此,希望调用新线程中的插槽的开发人员必须使用worker-object方法(参考本文QThread用法一);新插槽不应该直接实现到QThread子类中。
当子类化QThread时,请记住构造函数在旧线程中执行,而run()在新线程中执行。如果这两个函数访问同一个成员变量,属于从两个不同的线程访问该变量,要检查这样的操作是否线程安全。
注意:在跨线程的对象交互时必须小心。有关详细信息,请参见同步线程(Synchronizing Threads)。
管理线程
当线程started()和finished()时,QThread将发送一个信号通知你,你或者也可以用isFinished()和isRunning()来查询线程的状态。
你可以通过调用exit()或quit()来停止线程。在极端情况下,你可能希望强制terminate()一个正在执行的线程。然而,这样做是危险的,有关详细信息,请参阅terminate()和setTerminationEnabled()的文档。
从Qt 4.8开始,通过将finished()信号连接到QObject::deleteLater(),但线程运行结束时,会自动释放线程中定义的对象。
线程调用wait()可以阻塞本身,直到其它线程完成执行(或者超过指定的时间)。
QThread还提供了与平台无关的静态睡眠函数:sleep()、msleep()和usleep(),它们分别支持完整的秒、毫秒和微秒单位级别精度。这些函数在Qt 5.0中是公开的。
注意:因为Qt是一个事件驱动的框架,wait()和sleep()函数通常是不必要的。可以考虑用finished()信号来代替wait()函数,用QTimer类替代sleep()函数。
静态函数currentThreadId()和currentThread()返回当前执行线程的标识符。前者返回值是与平台相关的线程ID;后者是一个QThread指针。
你可以在启动线程之前,通过调用setObjectName()来设置线程名(如Linux的命令ps -L显示的)。如果没有调用setObjectName(),线程默认名是线程的类名。请注意,Windows的Release版本中目前还不支持。
枚举变量
enum QThread::Priority 这个枚举变量指明了操作系统如何调度新建立的线程