Qt::QThread->线程的使用

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 &);
  };
这是一段Qt的帮助文档中的代码。

在这段代码中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函数结束后退出。并且如果不调用exec()方法线程中不会产生Qt的事件循环。

线程示例存活在对其实例的父线程中,而不是该线程所调用的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指针。

 To choose the name that your thread will be given (as identified by the command ps -L on Linux, for example), you can call setObjectName() before starting the thread. If you don't call setObjectName(), the name given to your thread will be the class name of the runtime type of your thread object (for example, "RenderThread" in the case of the Mandelbrot Example, as that is the name of the QThread subclass). Note that this is currently not available with release builds on Windows. 

以上这段说明介绍了如何重新给线程命名,如不对线程进行命名,系统会默认其名字为类名称。这个特性目前只可以在linux平台上工作,在Windows下无效。


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在`onGetProtocolData()`函数中调用`AddDataToList()`函数,并使用`moveToThread`实现多线程,可以按照以下步骤进行操作: 1. 创建一个新的线程对象,并将其设置为`AddDataToList`函数的父对象。这将确保在新线程中执行`AddDataToList`函数。 ```cpp QThread* thread = new QThread(this); ``` 2. 创建一个新的对象来执行`AddDataToList`函数。这个对象应该是继承自`QObject`类,并且实现了`AddDataToList`函数的逻辑。 ```cpp class DataProcessor : public QObject { Q_OBJECT public: explicit DataProcessor(QObject* parent = nullptr) : QObject(parent) {} public slots: void AddDataToList(QStringList strList) { if(!ui->checkBox_4->checkState()) return; int row = ui->tableWidget->rowCount(); ui->tableWidget->insertRow(row); for(int i = 0; i < strList.count();i ++) { QTableWidgetItem *item = new QTableWidgetItem(strList.at(i),0); ui->tableWidget->setItem(row, i, item); if(i != strList.count() - 1) ui->tableWidget->item(row,i)->setTextAlignment(Qt::AlignCenter | Qt::AlignHCenter); } ui->tableWidget->scrollToBottom(); } }; ``` 3. 将新对象移动到新线程中。 ```cpp DataProcessor* dataProcessor = new DataProcessor(); dataProcessor->moveToThread(thread); ``` 4. 连接`onGetProtocolData()`函数和`AddDataToList()`函数。这将确保当`onGetProtocolData()`函数被调用时,`AddDataToList()`函数将在新线程中执行。 ```cpp connect(this, &UserWindow::onGetProtocolData, dataProcessor, &DataProcessor::AddDataToList); ``` 5. 启动新线程。 ```cpp thread->start(); ``` 最后,在`onGetProtocolData()`函数中,通过发送信号来调用`AddDataToList()`函数。 ```cpp emit onGetProtocolData(strList); ``` 这样,当调用`onGetProtocolData()`函数时,`AddDataToList()`函数将在新线程中执行。请注意,如果在`AddDataToList()`函数中有与GUI相关的操作,需要使用适当的方式进行跨线程通信,以确保线程安全。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值