Qt的信號槽連接機制如下:
- Qt::AutoConnection:默認,如果信號和槽在同一線程,使用DirectConnection;否則使用QueuedConnection。
- Qt::DirectConnection:槽函數立即在信號發出的線程執行,同步。
- Qt::QueuedConnection:槽函數在接收者的線程的事件循環中異步執行。
- Qt::BlockingQueuedConnection:類似Queued,但發送線程會阻塞直到槽執行完畢,不能在同一個線程中使用,否則死鎖。
- Qt::UniqueConnection:和Auto相同,但確保連接唯一。
- 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>
結果分析
| 連接方式 | 同線程行為 | 跨線程行為 | 原因解析 |
|---|---|---|---|
| 自動 | 同步阻塞 | 異步非阻塞 | 自動選擇直接/隊列連接 |
| 直接 | 同步阻塞 | 同步阻塞(槽函數在主線程執行) | 直接調用槽函數,跨線程時忽略線程邊界 |
| 隊列 | 同步執行(事件隊列優化) | 異步非阻塞 | 同線程隊列連接優化為直接調用,跨線程通過事件隊列異步處理 |
| 阻塞隊列 | 死鎖 | 同步阻塞 | 主線程等待子線程完成,但槽函數在子線程執行 |
| 唯一 | 同步阻塞 | 異步非阻塞 | 唯一連接不改變執行邏輯 |
| 單次 | 同步阻塞 | 異步非阻塞 | 連接自動斷開,首次行為同自動連接 |