Qt使用线程主要是通过QThread类来实现,实现方法主要有两种。1.通过使用moveToThread方法实现;2.通过继承QThread类实现。本文通过翻译Qt帮助文档来对QThread类进行简要说明。
QThread类提供一种与平台无关的线程管理方法。
在程序中一个QThread对象管理一个线程控制,线程开始于run方法。默认情况下,run方法通过调用exec方法来开始时间循环同时在这个线程下开启一个Qt的时间循环。
方法一:通过使用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类的槽函数与任意其他类的信号进行连接。由于使用了队列连接(queued connections)的机制,我们可以安全的将不同线程中的信号和槽进行连接。
方法二:集成QThread类并且重载run方法。以下是示例代码:
class WorkerThread : public QThread
{
Q_OBJECT
void run() Q_DECL_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()方法中。这就意味着所有该线程的队列槽函数将会在父线程中运行。如果我们要将槽函数放置在新的子线程中运行,那么必须使用moveToThread方法实现。需要注意的是槽函数不应该在继承了QThread的子类中直接实现。
当我们在继承QThread类的时候要明白:所构造的类中的对象运行在父线程中,而run()方法中的对象运行在子线程中。如果一个变量同时出现在两个函数中,并且这个变量被同时被两个线程使用,则需要检查这样做是不是安全的。
当一个新线程开始(start())或者完成(finished())的时候QThread类将会通过发送一个信号来表明自己的状态。另外也可以通过isFinished()和isRunning()方法来检查线程的状态。
我们可以通过使用exit()和quit()方法来停止线程。在极端情况下,我们或许希望强行终结一个进程。然而这样做是危险并且最好不要如此。
在Qt4.8版本以后,可以通过将finished()信号连接到QObject::deleteLater()上来释放一个刚刚结束的线程。
使用wait()方法来阻塞线程,直到另一个线程运行完成(或者直到经过一个特定的时间)。
QThread还提供一个静态的与平台无关的休眠函数:sleep(),msleep(),usleep()分别支持秒、毫秒和微秒的分辨率。这些函数从Qt5.0开始支持。
NOTE:一般来说wait()和sleep()函数并不是必要的,因为Qt是一个事件驱动的框架。可以通过接收finished()信号来取代wait()的使用,通过使用QTimer来取代sleep()函数的使用。
使用currentThreadId()和currentThread()函数可以返回当前线程的标识符。前者返回线程的ID号,后者返回一个QThread指针。