1. 程式人生 > 其它 >利用Qt訊號槽和std::function將任務放在主執行緒(GUI執行緒)執行

利用Qt訊號槽和std::function將任務放在主執行緒(GUI執行緒)執行

  1. 宣告一個新增任務介面,訊號和槽函式各兩個(返回型別void和非void)。新增任務介面的引數為std::function型別。訊號與槽函式的第一個引數是std::function型別的任務,第二個引數則為任務的返回值。
class TestQtMainThread : public QWidget {
  Q_OBJECT

 public:
  TestQtMainThread(QWidget* parent = Q_NULLPTR);

  template <typename T>
  T AppendTask(const std::function<T()>& task);

 signals:
  void SIGAppendTask(const std::function<QVariant()>& task, QVariant& ret);
  void SIGAppendTask(const std::function<void()>& task);

 private slots:
  void ONAppendTask(const std::function<QVariant()>& task, QVariant& ret);
  void ONAppendTask(const std::function<void()>& task);

 private:
  Ui::TestQtMainThreadClass ui;
};
  1. 使用Qt::BlockingQueuedConnection方式連線訊號與槽函式
  connect(this,
          qOverload<const std::function<QVariant()> &, QVariant &>(
              &TestQtMainThread::SIGAppendTask),
          this,
          qOverload<const std::function<QVariant()> &, QVariant &>(
              &TestQtMainThread::ONAppendTask),
          Qt::BlockingQueuedConnection);

  connect(
      this,
      qOverload<const std::function<void()> &>(
          &TestQtMainThread::SIGAppendTask),
      this,
      qOverload<const std::function<void()> &>(&TestQtMainThread::ONAppendTask),
      Qt::BlockingQueuedConnection);
  1. 槽函式定義
void TestQtMainThread::ONAppendTask(const std::function<QVariant()> &task,
                                    QVariant &ret) {
  ret = task();
}

void TestQtMainThread::ONAppendTask(const std::function<void()> &task) {
  task();
}
  1. 介面定義
template <typename T>
inline T TestQtMainThread::AppendTask(const std::function<T()>& task) {
  // 訊號槽無法對沒特化的tempalte進行connect,使用QVariant轉換
  // 轉換包括:1.使用lambda包裹task轉換其返回型別;2.轉換引數型別
  QVariant ret;
  // 避免BlockingQueuedConnection死鎖
  // this->thread() == QCoreApplication::instance()->thread()
  if (QThread::currentThread() == this->thread()) {
    ONAppendTask([&]() { return QVariant::fromValue<T>(task()); }, ret);
  } else {
    emit SIGAppendTask([&]() { return QVariant::fromValue<T>(task()); }, ret);
  }
  return ret.value<T>();
}

// void特化版
template <>
inline void TestQtMainThread::AppendTask(const std::function<void()>& task) {
  if (QThread::currentThread() == this->thread()) {
    ONAppendTask(task);
  } else {
    emit SIGAppendTask(task);
  }
}
  1. 使用
// WorkerThread是QThread子類,此段函式在非主執行緒執行
void WorkerThread::run() {
  auto result = main_thread_->AppendTask<QString>([]() {
    if (QThread::currentThread() != QCoreApplication::instance()->thread()) {
      return "error!";
    }
    return "ok!";
  });
  qDebug() << result;

  // QWidget::move(...),此函式不允許在非主執行緒呼叫
  main_thread_->AppendTask<void>([&]() { main_thread_->move({0, 0}); });
}

覆蓋QWidget::move(...),非子執行緒可直接呼叫TestQtMainThread::move(...)

void TestQtMainThread::move(const QPoint &pos) {
  this->AppendTask<void>([&] { this->QWidget::move(pos); });
}