Qt扫盲-QThread使用总结

一、概述

一个 QThread 管理程序中的一个线程。QThreads在run()中开始执行。默认情况下,run() 通过调用 exec() 来启动事件循环,并在线程中运行Qt事件循环。 在这个 QThread 属于Qt 线程支持的低级API,同时 QThread 是跨平台的,因为 QThread 封装的是 本地的 线程库,就像 Windows 下是 win32 thread, 或者Linux下的 pthread。同时它们可以与相同本机API的线程一起使用。QThread是Qt中所有线程控制的基础,每个QThread实例代表并控制一个线程。

QThread可以直接实例化,也可以子类化。有两个使用方法

  • 实例化一个 QThread 提供了一个并行事件循环环境,在环境里可以在第二个线程中调用QObject槽函数。
  • 子类化一个QThread可以让应用程序在开始它的事件循环之前初始化一个新的线程,或者在没有事件循环的情况下运行并行代码。

也就是下面的两个方式

二、使用方式

下面的例子就是使用QObject::moveToThread()将worker对象移动到线程中。Worker槽函数中的代码会在一个单独的线程中执行。

我们可以自由地将Worker的槽连接到任何线程中的任何对象的任何信号。由于一种称为排队连接(queued connections)的机制,在不同的线程之间连接信号和槽是安全的。

1. 方式一

//在线程中待执行的代码
class Worker : public QObject
{
      Q_OBJECT

  public slots:
      void doWork(const QString &parameter) {
          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 &)}

2. 方式二

另一种让代码在单独的线程中运行的方法是子类化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()在新线程中执行。如果在两个函数中都访问成员变量,那么访问该变量的是两个不同的线程。检查这样做是否安全。

三、线程管理

1. 线程状态及信息

当线程 started() 和 finished() 时 ,QThread将通过信号通知您,或者您可以使用 isFinished() 和 isRunning() 来查询线程的状态。

线程的优先级可以通过 priority() 来获取,也可以通过 setPriority() 来设置执行的优先级,一般不会经常使用的。

静态函数 currentThreadId() 和 currentThread() 返回当前执行线程的标识符。前者为线程返回平台特定的ID;后者返回一个QThread指针。

要选择为线程指定的名称(例如,在Linux上由命令ps -L标识),可以在启动线程之前调用 setObjectName() 。如果不调用setObjectName(),给线程的名称将是线程对象的运行时类型的类名。

请注意,目前在Windows上的release版本中不可用。

2. 线程退出

可以调用 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(),可以释放生活在刚刚结束的线程中的对象。

3. 线程休眠

QThread 可以使用 wait() 来阻塞正在调用的线程,直到另一个线程完成执行(或超过指定的时间)。

QThread还提供了静态的、平台无关的休眠函数:sleep()、msleep()和usleep()分别允许完整的秒级、毫秒级和微秒级分辨率。这些函数在Qt 5.0中是公开的。
注意:一般来说,wait()和sleep()函数应该是不必要的,因为Qt是一个事件驱动框架。考虑监听finished()信号,而不是wait()。考虑使用QTimer代替sleep()函数。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Qt使用 QThread 实现多线程可以分为以下步骤: 1. 创建一个继承自 QThread 的子类,重写其 run() 方法,该方法将在新的线程中执行。 2. 在子类中定义信号和槽,以便在主线程和子线程之间进行通信。 3. 在主线程中创建子线程对象,并使用 start() 方法启动线程。 4. 在主线程中使用 connect() 方法将子线程的信号与主线程的槽连接起来,以实现跨线程通信。 以下是一个简单的示例: ```c++ #include <QThread> #include <QDebug> class MyThread : public QThread { Q_OBJECT signals: void mySignal(QString message); protected: void run() override { qDebug() << "Thread started"; emit mySignal("Hello from thread"); qDebug() << "Thread finished"; } }; class MyClass : public QObject { Q_OBJECT public slots: void mySlot(QString message) { qDebug() << "Received message:" << message; } }; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); MyThread thread; MyClass obj; QObject::connect(&thread, &MyThread::mySignal, &obj, &MyClass::mySlot); thread.start(); return a.exec(); } #include "main.moc" ``` 在这个例子中,我们定义了一个 MyThread 类,它继承自 QThread,重写了 run() 方法,并定义了一个 mySignal 信号。我们还定义了一个 MyClass 类,它有一个名为 mySlot 的槽,用于接收 MyThread 发出的信号。在 main() 函数中,我们创建了一个 MyThread 对象和一个 MyClass 对象,并使用 connect() 方法将 mySignal 信号连接到 mySlot 槽。然后,我们调用 start() 方法来启动线程。当 run() 方法被执行时,它将发出 mySignal 信号,并传递一个字符串参数。这个信号将被发送到主线程,并调用 mySlot 槽来处理它。最后,我们调用 a.exec() 来启动 Qt 的事件循环,以便在主线程中接收信号和执行槽函数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

太阳风暴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值