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>

结果分析

连接方式同线程行为跨线程行为原因解析
自动同步阻塞异步非阻塞自动选择直接/队列连接
直接同步阻塞同步阻塞(槽函数在主线程执行)直接调用槽函数,跨线程时忽略线程边界
队列同步执行(事件队列优化)异步非阻塞同线程队列连接优化为直接调用,跨线程通过事件队列异步处理
阻塞队列死锁同步阻塞主线程等待子线程完成,但槽函数在子线程执行
唯一同步阻塞异步非阻塞唯一连接不改变执行逻辑
单次同步阻塞异步非阻塞连接自动断开,首次行为同自动连接

点墨
26 声望3 粉丝

全栈前端开发工程师