Qt信号槽的几种连接方式:AutoConnection/DirectConnection/QueuedConnection/BlockingQueuedConnection

发布时间:2026/7/7 1:49:54

Qt信号槽的几种连接方式:AutoConnection/DirectConnection/QueuedConnection/BlockingQueuedConnection 目录前言Qt信号槽的5种连接方式AutoConnection自动连接DirectConnection直连QueuedConnection队列连接BlockingQueuedConnection阻塞队列连接UniqueConnection唯一连接几种连接方式对比一个包含5种连接方式的完整示例总结参考链接非AI生成觉得有用就请您帮忙点赞转发收藏吧您的鼓励是我创作的动力多谢看官。前言Qt的信号槽机制是Qt的最显著特征信号和槽函数是通过connect函数连接的。由于信号的发送线程和接收线程可能不同Qt5提供了5种信号槽连接方式供程序使用深刻理解这几种连接方式的使用场景和区别有助于我们编写更健壮的Qt程序避免一些不必要的麻烦。Qt信号槽的5种连接方式AutoConnection自动连接若信号发送者和接收者在相同线程使用DirectConnection若信号发送者和接收者在不同线程使用QueueConnection。DirectConnection直连信号发送者和接收者在相同线程发射信号后立即在当前线程同步调用槽函数如果槽函数还没执行完成则会造成线程阻塞。槽函数在发射信号的线程执行多线程使用直连模式风险很高。如果槽函数操作了UI就相当于在子线程操作UI结果就是程序随机崩溃或其他非常严重的问题UI只能在主线程操作子线程绝对不能操作UI这是Qt的铁律。多线程信号槽连接严禁使用直连方式。QueuedConnection队列连接多线程首选队列连接方式槽函数在接收者所在线程执行不阻塞发送者线程。信号发射是“立刻完成的”槽函数执行是“延后的、由接收者的事件循环调度的”。Qt::QueuedConnection的信号发射只是向接收者线程投递一个事件槽函数只有在接收者线程的事件循环轮询并处理该事件时才会执行。BlockingQueuedConnection阻塞队列连接多线程备选连接方式阻塞的是“信号发送者所在的线程”而不是接收者线程。具体机制如下1.发送者线程阻塞当发射信号时发送者线程会把事件投递到接收者线程的事件队列然后自身通过内部信号量QSemaphore进入阻塞等待状态不会继续执行后面的代码。2.接收者线程正常执行接收者线程的事件循环照常取出该事件在接收者所在线程执行槽函数。槽函数执行完毕后内部会释放信号量通知发送者线程。3.解除阻塞接收者槽函数返回后发送者线程被唤醒从 emit处继续执行。特别注意必须跨线程发送者和接收者绝对不能在同一个线程否则发送者线程会等自己处理事件导致死锁。慎用场景常用于需要跨线程同步结果的场景但因容易引发死锁或界面卡顿如子线程阻塞主线程日常应优先使用普通的 Qt::QueuedConnection。UniqueConnection唯一连接Qt 信号槽机制中的特殊标志位用于确保‌相同的发送者、信号、接收者和槽之间仅建立一次连接‌重复调用connect会失败并返回false从而防止重复触发和内存泄漏 。‌‌几种连接方式对比一个包含5种连接方式的完整示例#ifndef MAINWINDOW_H #define MAINWINDOW_H #include QMainWindow #include QThread #include QMutex #include QPointer #include QMessageBox #include Utility.h #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(); private slots: void on_btnSetConnectionType_clicked(); private: Ui::MainWindow *ui; QThread* m_thread nullptr; Worker* m_worker nullptr; QPointerQThread m_thread_guard nullptr; QPointerWorker m_worker_guard nullptr; private: bool stop(); void startConnectionTest(); private slots: void onMessageReceived(ThreadType threadType,const QString msg); void on_btnClear_clicked(); }; #endif // MAINWINDOW_H#include mainwindow.h #include ui_mainwindow.h MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui-setupUi(this); } MainWindow::~MainWindow() { delete ui; } void MainWindow::startConnectionTest() { int itemIndex ui-combConnType-currentIndex(); QString itemText ui-combConnType-currentText(); itemText QString(信号槽连接方式%1).arg(itemText); Qt::ConnectionType connType Qt::AutoConnection; switch (itemIndex) { case 0: connType Qt::AutoConnection; break;//槽函数自动选择执行的线程 //槽函数在发射信号的线程执行多线程使用直连模式风险很高 //如果槽函数操作了UI就相当于在子线程操作UI结果就是程序随机崩溃或其他非常严重的问题UI只能在主线程操作子线程绝对不能操作UI这是Qt的铁律 //多线程信号槽连接严禁使用直连方式 case 1: connType Qt::DirectConnection; break; //多线程首选队列连接方式槽函数在接收者所在线程执行不阻塞发送者线程 //信号发射是“立刻完成的”槽函数执行是“延后的、由接收者的事件循环调度的” //Qt::QueuedConnection的信号发射只是向接收者线程投递一个事件槽函数只有在接收者线程的事件循环轮询并处理该事件时才会执行。 case 2: connType Qt::QueuedConnection; break; //多线程次选连接方式阻塞的是“信号发送者所在的线程”而不是接收者线程 //特别注意必须跨线程发送者和接收者绝对不能在同一个线程否则发送者线程会等自己处理事件导致死锁。 //慎用场景常用于需要跨线程同步结果的场景但因容易引发死锁或界面卡顿如子线程阻塞主线程日常应优先使用普通的 Qt::QueuedConnection /* 具体机制如下 发送者线程阻塞当发射信号时发送者线程会把事件投递到接收者线程的事件队列然后自身通过内部信号量QSemaphore进入阻塞等待状态不会继续执行后面的代码。 接收者线程正常执行接收者线程的事件循环照常取出该事件在接收者所在线程执行槽函数。槽函数执行完毕后内部会释放信号量通知发送者线程。 解除阻塞接收者槽函数返回后发送者线程被唤醒从 emit处继续执行。 */ case 3: connType Qt::BlockingQueuedConnection; break; case 4: connType Qt::UniqueConnection; break; default:connType Qt::AutoConnection;break; } if(1 itemIndex){ QMessageBox::critical(this,系统提示,多线程使用DirectConnection连接信号槽是高危行为会导致程序闪退\n您头铁要试试看吗); } onMessageReceived(ThreadType::UnKnown,itemText); m_thread new QThread; m_worker new Worker(nullptr,Worker1,5,ThreadType::MoveToThread);//parent必须传nullptr否则会违背Qt的线程亲和性规则出现各种严重问题 connect(m_thread,QThread::started,m_worker,Worker::Start); connect(m_worker,Worker::messageReceived,this,MainWindow::onMessageReceived,connType); m_worker_guard m_worker;//使用QPointerWorker防悬空指针误用 m_thread_guard m_thread;//使用QPointerQThread防悬空指针误用 connect(m_worker,Worker::finished,this,[](){ if(m_worker_guard){ m_worker_guard-deleteLater(); QString msg QString(%1 Worker对象释放成功).arg(Utility::getNowTimeStrWithMs()); onMessageReceived(ThreadType::UnKnown,msg); } if(m_thread_guard){ m_thread_guard-quit(); } }); connect(m_thread,QThread::finished,this,[](){ if(m_thread_guard){ m_thread_guard-deleteLater(); QString msg QString(%1 QThread对象释放成功).arg(Utility::getNowTimeStrWithMs()); onMessageReceived(ThreadType::UnKnown,msg); } }); m_worker-moveToThread(m_thread); m_thread-start(); } void MainWindow::onMessageReceived(ThreadType threadType, const QString msg) { static QMutex mutex; QMutexLockerQMutex locker(mutex); ui-tbLog-appendPlainText(msg); } void MainWindow::on_btnSetConnectionType_clicked() { if(stop()){ startConnectionTest(); } } //不建议强制停止正在运行的线程容易出现内存泄漏或其他问题 bool MainWindow::stop() { if(m_thread_guard){ if(m_thread_guard-isRunning()){ QMessageBox::information(this,系统提示,线程正在执行请等待线程执行完成); return false; } m_thread_guard-quit(); m_thread_guard-wait(); } else{ qDebug()QThread对象已经释放了可以安全开启新线程了; return true; } return false; } void MainWindow::on_btnClear_clicked() { ui-tbLog-clear(); }总结如果能确定信号发送线程和接收线程可以明确指定连接方式大多数场景可以使用Qt::AutoConnectionQt会自动帮您选择连接方式。参考链接别再乱连了Qt信号槽重复连接的坑我用Qt::UniqueConnection帮你填平深入解析 QObject::connect 的五种连接类型

相关新闻