博客 / 詳情

返回

Qt connectiontype

Qt的信號槽連接機制如下:

image.png

  1. Qt::AutoConnection:默認,如果信號和槽在同一線程,使用DirectConnection;否則使用QueuedConnection。
  2. Qt::DirectConnection:槽函數立即在信號發出的線程執行,同步。
  3. Qt::QueuedConnection:槽函數在接收者的線程的事件循環中異步執行。
  4. Qt::BlockingQueuedConnection:類似Queued,但發送線程會阻塞直到槽執行完畢,不能在同一個線程中使用,否則死鎖。
  5. Qt::UniqueConnection:和Auto相同,但確保連接唯一。
  6. Qt::SingleShotConnection:槽只觸發一次,之後自動斷開。

代碼

worker.h

#ifndef WORKER_H
#define WORKER_H

#include <QObject>

class Worker : public QObject {
    Q_OBJECT
  public:
    explicit Worker(QObject* parent = nullptr);

  public slots:
    void doWork();

  signals:
    void resultReady(const QString& string);
};

#endif  // WORKER_H

worker.cpp

#include "worker.h"

#include <QDateTime>
#include <QDebug>
#include <QThread>

Worker::Worker(QObject* parent) : QObject{parent} { qDebug() << "worker" << this << this->thread(); }

void Worker::doWork() {
    qDebug() << "do work" << this << this->thread();

    QThread::sleep(3);

    QString result = "Task completed at" + QDateTime::currentDateTime().toString();

    qDebug() << result;

    emit resultReady(result);
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include "worker.h"

QT_BEGIN_NAMESPACE
namespace Ui {
    class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow {
    Q_OBJECT

  public:
    MainWindow(QWidget* parent = nullptr);
    ~MainWindow();

    void disconnect();

  private slots:
    void on_pushButton_2_clicked();

    void on_stst_clicked();

    void on_stdt_clicked();

    void on_pushButton_3_clicked();

    void on_pushButton_clicked();

    void on_pushButton_4_clicked();

    void on_pushButton_5_clicked();

    void on_pushButton_6_clicked();

signals:
    void sameThreadSend();
    void diffThreadSend();

  private:
    Ui::MainWindow* ui;
    QThread* m_workThread;
    Worker* m_workerSameThread;
    Worker* m_workerDiffThread;
};
#endif  // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"

#include <QThread>

#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);

    qDebug() << "MainWindow" << this << this->thread();

    m_workThread = new QThread(this);

    m_workerSameThread = new Worker(this);

    m_workerDiffThread = new Worker();

    m_workerDiffThread->moveToThread(m_workThread);

    m_workThread->start();
}

MainWindow::~MainWindow() {
    delete ui;
    m_workThread->exit();
    m_workThread->deleteLater();
}

void MainWindow::on_stst_clicked() {
    qDebug() << "同線程" << this->thread();

    qDebug() << "before";

    emit this->sameThreadSend();

    qDebug() << "after";
}

void MainWindow::on_stdt_clicked() {
    qDebug() << "不同線程" << this->thread();

    qDebug() << "before";

    emit this->diffThreadSend();

    qDebug() << "after";
}

void MainWindow::disconnect() {
    qDebug() << "斷開連接";

    QObject::disconnect(this, &MainWindow::sameThreadSend, m_workerSameThread, &Worker::doWork);
    QObject::disconnect(this, &MainWindow::diffThreadSend, m_workerDiffThread, &Worker::doWork);
}

void MainWindow::on_pushButton_clicked() {
    disconnect();

    qDebug() << "自動連";

    connect(this, &MainWindow::sameThreadSend, m_workerSameThread, &Worker::doWork, Qt::AutoConnection);
    connect(this, &MainWindow::diffThreadSend, m_workerDiffThread, &Worker::doWork, Qt::AutoConnection);
}

void MainWindow::on_pushButton_2_clicked() {
    disconnect();

    qDebug() << "直連";

    connect(this, &MainWindow::sameThreadSend, m_workerSameThread, &Worker::doWork, Qt::DirectConnection);
    connect(this, &MainWindow::diffThreadSend, m_workerDiffThread, &Worker::doWork, Qt::DirectConnection);
}

void MainWindow::on_pushButton_3_clicked() {
    disconnect();

    qDebug() << "queue連";

    connect(this, &MainWindow::sameThreadSend, m_workerSameThread, &Worker::doWork, Qt::QueuedConnection);
    connect(this, &MainWindow::diffThreadSend, m_workerDiffThread, &Worker::doWork, Qt::QueuedConnection);
}

void MainWindow::on_pushButton_4_clicked() {
    disconnect();

    qDebug() << "阻塞連";

    connect(this, &MainWindow::sameThreadSend, m_workerSameThread, &Worker::doWork, Qt::BlockingQueuedConnection);
    connect(this, &MainWindow::diffThreadSend, m_workerDiffThread, &Worker::doWork, Qt::BlockingQueuedConnection);
}

void MainWindow::on_pushButton_5_clicked() {
    disconnect();

    qDebug() << "unique連";

    connect(this, &MainWindow::sameThreadSend, m_workerSameThread, &Worker::doWork, Qt::UniqueConnection);
    connect(this, &MainWindow::diffThreadSend, m_workerDiffThread, &Worker::doWork, Qt::UniqueConnection);
}

void MainWindow::on_pushButton_6_clicked()
{
    disconnect();

    qDebug() << "singleshot連";

    connect(this, &MainWindow::sameThreadSend, m_workerSameThread, &Worker::doWork, Qt::SingleShotConnection);
    connect(this, &MainWindow::diffThreadSend, m_workerDiffThread, &Worker::doWork, Qt::SingleShotConnection);
}

main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QPushButton" name="stst">
    <property name="geometry">
     <rect>
      <x>20</x>
      <y>50</y>
      <width>141</width>
      <height>31</height>
     </rect>
    </property>
    <property name="text">
     <string>Start Task SameThread</string>
    </property>
   </widget>
   <widget class="QPushButton" name="pushButton_2">
    <property name="geometry">
     <rect>
      <x>100</x>
      <y>10</y>
      <width>75</width>
      <height>23</height>
     </rect>
    </property>
    <property name="text">
     <string>直連</string>
    </property>
   </widget>
   <widget class="QPushButton" name="pushButton_3">
    <property name="geometry">
     <rect>
      <x>180</x>
      <y>10</y>
      <width>75</width>
      <height>23</height>
     </rect>
    </property>
    <property name="text">
     <string>queue連</string>
    </property>
   </widget>
   <widget class="QPushButton" name="pushButton_4">
    <property name="geometry">
     <rect>
      <x>260</x>
      <y>10</y>
      <width>75</width>
      <height>23</height>
     </rect>
    </property>
    <property name="text">
     <string>阻塞連</string>
    </property>
   </widget>
   <widget class="QPushButton" name="pushButton_5">
    <property name="geometry">
     <rect>
      <x>350</x>
      <y>10</y>
      <width>75</width>
      <height>23</height>
     </rect>
    </property>
    <property name="text">
     <string>Unique連</string>
    </property>
   </widget>
   <widget class="QPushButton" name="pushButton_6">
    <property name="geometry">
     <rect>
      <x>430</x>
      <y>10</y>
      <width>101</width>
      <height>21</height>
     </rect>
    </property>
    <property name="text">
     <string>SingleShot連</string>
    </property>
   </widget>
   <widget class="QPushButton" name="stdt">
    <property name="geometry">
     <rect>
      <x>20</x>
      <y>90</y>
      <width>141</width>
      <height>31</height>
     </rect>
    </property>
    <property name="text">
     <string>Start Task DiffThread</string>
    </property>
   </widget>
   <widget class="QPushButton" name="pushButton">
    <property name="geometry">
     <rect>
      <x>20</x>
      <y>10</y>
      <width>75</width>
      <height>23</height>
     </rect>
    </property>
    <property name="text">
     <string>自動連</string>
    </property>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>21</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

結果分析

連接方式 同線程行為 跨線程行為 原因解析
自動 同步阻塞 異步非阻塞 自動選擇直接/隊列連接
直接 同步阻塞 同步阻塞(槽函數在主線程執行) 直接調用槽函數,跨線程時忽略線程邊界
隊列 同步執行(事件隊列優化) 異步非阻塞 同線程隊列連接優化為直接調用,跨線程通過事件隊列異步處理
阻塞隊列 死鎖 同步阻塞 主線程等待子線程完成,但槽函數在子線程執行
唯一 同步阻塞 異步非阻塞 唯一連接不改變執行邏輯
單次 同步阻塞 異步非阻塞 連接自動斷開,首次行為同自動連接
user avatar cunyu1943 頭像 redorblack 頭像 buxiyan 頭像 yihan123 頭像 taoqun 頭像 wuyuedexingkong 頭像 lilin_5e390e08b42e4 頭像 bell_lemon 頭像 liuxuan_5845129fbf248 頭像 pingan8787 頭像 donnytab 頭像
11 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.