如何正确使用 QThread

简述

要实现一个线程很简单,写一个函数,绑定一些数据。如果有必要的话,可以使用 mutex 或者其他方法来保证和线程的安全交互。

在 Qt 中,使用线程有几种不同的方式,下面主要演示 QThread + QObject(worker),此方式主要用于涉及事件驱动的编程和跨线程的信号/槽。

| 版权声明:一去、二三里,未经博主允许不得转载。

worker-object

在使用 worker-object 时,最主要的事情是要记住 QThread 不是一个线程,而是一个线程对象的包装器。这个包装器提供了信号、槽和方法,来轻松地使用 Qt 中的线程对象。

具体的使用,分为以下几步:

  • 准备一个 QObject 子类,其中包含了所有需要的功能。
  • 创建一个 QThread 实例,使用 QObject::moveToThread(QThread*) 将 QObject 对象移动至线程中。
  • 设置适当的信号/槽连接,以保证正常退出。
  • 调用 QThread::start() 启动线程

声明 Worker 类

来实现一个简单的 Worker 类:

class Worker : public QObject {
    Q_OBJECT

public:
    Worker();
    ~Worker();

public slots:
    void process();

signals:
    void finished();
    void error(QString err);

private:
    // 在此处添加变量
};

至少需要添加一个公有的槽函数,用于触发实例。一旦线程启动,就立刻处理数据。

现在,看看这个类的基本实现:

Worker::Worker() {
    // 这里,可以将构造函数参数的数据复制到内部变量
}

Worker::~Worker() {
    // 释放资源
}

// 开始处理数据
void Worker::process() {
    // 这里,使用 new 分配资源
    qDebug("Hello World!");
    emit finished();
}

虽然 Worker 类没做什么特别的事情,但它包含了所有必要的元素。当主函数(这里是 process())被调用时,就开始处理数据。一旦处理完成,就会发射 finished() 信号,以触发 QThread 实例的关闭。

这里需要注意一点:永远不要在 QObject 类的构造函数中分配堆对象(使用 new),因为这个分配是在主线程上执行的,而不是在新的 QThread 实例上执行的。也就是说,新创建的对象将由主线程所拥有,而非 QThread 实例。这会使代码无法正常工作。相反,在这种情况下,应该在主函数(process())中分配资源,因为当被调用时,对象将在新的线程实例上,因此它将拥有资源。

创建 Worker 实例

现在,来看看如何创建一个 Worker 实例,并把它放到一个 QThread 实例中:

QThread *thread = new QThread();
Worker *worker = new Worker();
worker->moveToThread(thread);

// 错误处理
connect(worker, SIGNAL(error(QString)), this, SLOT(errorString(QString)));

// 处理数据
connect(thread, SIGNAL(started()), worker, SLOT(process()));

// 退出、删除
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

// 启动线程
thread->start();

注意: connect() 系列是最为关键的部分。

当 worker 实例发出 finished() 信号时,就会通知线程退出。然后,使用相同的信号来删除 worker 实例。

最后,为了防止 crash(有可能当 thread 被删除时,还没有完全关闭),需要将 thread(非 worker)的 finished() 信号连接到其自身的 deleteLater() 槽函数。这样以来,只有线程在完全退出时,才会被删除。

你可能感兴趣的:(Qt,《Qt,实战一二三》)