一个 QThread 管理程序中的一个线程。QThreads在run()中开始执行。默认情况下,run() 通过调用 exec() 来启动事件循环,并在线程中运行Qt事件循环。 在这个 QThread 属于Qt 线程支持的低级API,同时 QThread 是跨平台的,因为 QThread 封装的是 本地的 线程库,就像 Windows 下是 win32 thread, 或者Linux下的 pthread。同时它们可以与相同本机API的线程一起使用。QThread是Qt中所有线程控制的基础,每个QThread实例代表并控制一个线程。
QThread可以直接实例化,也可以子类化。有两个使用方法
也就是下面的两个方式
下面的例子就是使用QObject::moveToThread()将worker对象移动到线程中。Worker槽函数中的代码会在一个单独的线程中执行。
我们可以自由地将Worker的槽连接到任何线程中的任何对象的任何信号。由于一种称为排队连接(queued connections)的机制,在不同的线程之间连接信号和槽是安全的。
//在线程中待执行的代码
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 &);
};
另一种让代码在单独的线程中运行的方法是子类化QThread并重新实现run()。例如:
//重载 QThread 实现自己的线程功能
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对象上调用的方法将在调用该方法的线程中执行。当子类化QThread时,记住构造函数在旧线程中执行,而run()在新线程中执行。如果在两个函数中都访问成员变量,那么访问该变量的是两个不同的线程。检查这样做是否安全。
当线程 started() 和 finished() 时 ,QThread将通过信号通知您,或者您可以使用 isFinished() 和 isRunning() 来查询线程的状态。
线程的优先级可以通过 priority() 来获取,也可以通过 setPriority() 来设置执行的优先级,一般不会经常使用的。
静态函数 currentThreadId() 和 currentThread() 返回当前执行线程的标识符。前者为线程返回平台特定的ID;后者返回一个QThread指针。
要选择为线程指定的名称(例如,在Linux上由命令ps -L标识),可以在启动线程之前调用 setObjectName() 。如果不调用setObjectName(),给线程的名称将是线程对象的运行时类型的类名。
请注意,目前在Windows上的release版本中不可用。
可以调用 exit() 或 quit() 来停止线程。在极端情况下,你可能想要 强制终止 terminate() 一个正在执行的线程。但有一些注意事项
terminate() 终止线程的执行。线程可以立即终止,也可以不立即终止,这取决于操作系统的调度策略。在terminate()之后使用QThread::wait()来确定。
当线程终止时,所有等待线程结束的线程将被唤醒。
Qt官方说:此函数是危险的,不鼓励使用。线程可以在其代码路径的任何位置终止。线程可以在修改数据时终止。线程没有机会进行自我清理,解锁任何持有的互斥量等。简而言之,仅在绝对必要时使用该函数。
可以通过调用QThread::setTerminationEnabled()显式启用或禁用终止。当终止被禁用时调用这个函数会导致延迟终止,直到终止被重新启用。
QThread::setTerminationEnabled() 的解释是 根据enabled参数启用或禁用当前线程的终止。线程必须是由QThread启动的。当enabled为false时,禁用终止。将来对QThread::terminate()的调用将立即返回,而不会产生任何影响。相反,终止被推迟到启用终止。
当enabled为true时,启用终止。以后对QThread::terminate()的调用将正常地终止线程。如果终止被延迟(即QThread::terminate()被禁用终止调用),此函数将立即终止调用线程。注意,这个函数在这种情况下不会返回。
从Qt 4.8开始,通过将 finished() 信号连接到 QObject::deleteLater(),可以释放生活在刚刚结束的线程中的对象。
QThread 可以使用 wait() 来阻塞正在调用的线程,直到另一个线程完成执行(或超过指定的时间)。
QThread还提供了静态的、平台无关的休眠函数:sleep()、msleep()和usleep()分别允许完整的秒级、毫秒级和微秒级分辨率。这些函数在Qt 5.0中是公开的。
注意:一般来说,wait()和sleep()函数应该是不必要的,因为Qt是一个事件驱动框架。考虑监听finished()信号,而不是wait()。考虑使用QTimer代替sleep()函数。