【Qt中信号槽连接connect有接收者和无接收者的区别】

Qt中信号槽连接connect有接收者和无接收者的区别

概述

在qt开发过程中,在多线程的环境下,发现程序有些问题,根据程序打印出来的提示,发现与线程有关。最终定位到connect函数处,于是有以下的研究:关于connect函数有接收者和无接收者的区别。

代码验证

验证工程代码文件包括:define.h(类的定义) mainwindow类文件。

根据打印结果(可以看mainwindow.cpp的注释),可以得出结论(也在注释里面)

注意:尽量使用有接收者的connect函数,同时,信号方的线程指的是发送信号的线程,而不是信号拥有者的线程。
define.h:

cpp 复制代码
#ifndef DEFINE_H
#define DEFINE_H
#include<QDebug>
#include<QDateTime>
#pragma execution_character_set("utf-8") //如果源文件是UTF-8+无BOM的编码方式,则一定不能加#pragma execution_character_set("utf-8")
#define myDebug qDebug()<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")<<__FILE__<<__LINE__
#include<QThread>
class Worker: public QObject
{
    Q_OBJECT
public:
    Worker(QObject* parent = nullptr): QObject(parent) {}

signals:
    void finished();

public slots:
    void work()
    {
        myDebug << "工作完成 worker";
        myDebug << QThread::currentThread() << " ==" << sender()->thread() << "==" << this->thread() << this->metaObject()->className();
        emit finished();
    }
};

class Worker2: public QObject
{
    Q_OBJECT
public:
    Worker2(QObject*parent = nullptr): QObject(parent)
    {

    }
    void emitAfterWork()
    {
        emit afterWork();
    }
signals:
    void afterWork();
public slots:
    void finishedSlots()
    {
        myDebug << "工作完成,下班,Work2";
        myDebug << QThread::currentThread() << " ==" << sender()->thread() << "==" << this->thread() << this->metaObject()->className();
        emit afterWork();
    }
public:

};
#endif // DEFINE_H

mainwindow.h:

cpp 复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include"define.h"
QT_BEGIN_NAMESPACE
namespace Ui
{
    class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private:
    Ui::MainWindow* ui;
    Worker* wk;
    Worker2* wk2;
};
#endif // MAINWINDOW_H

mainwindow.cpp:

cpp 复制代码
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    myDebug;
    wk2 = new Worker2;
    wk = new Worker;
    QThread *thread = new QThread;
    wk->moveToThread(thread);
    thread->start();
    connect(ui->pushButton, &QPushButton::released, wk, &Worker::work);//按钮会触发Worker类的finished信号,且是在Worker所在线程的。

    //wk与当前类对象不在同一线程:wk在子线程,而当前类对象在主线程。
    //无接收者的connect,槽函数的执行线程是由信号端决定的,打印结果:QThread(0x6c80c8)  == QThread(0x6c80c8) == QThread(0x6c5f50) MainWindow
    //结果表明,在没有接收者的情况下,槽函数所在线程就是信号端的线程(至于是发送者的线程  还是  执行发送信号的线程要从下面另一个connect确定)

    //有接收者的connect,槽函数与接收者在同一个线程,打印结果:QThread(0x13ed680)  == QThread(0x13f8748) == QThread(0x13ed680) MainWindow
    //结果表明,在接收者的情况下,槽函数所在线程就是接收者的线程
    connect(wk, &Worker::finished, /*this,*/ [ = ]()
    {
        myDebug << "工作完成,下班,Work2";
        myDebug << QThread::currentThread() << " ==" << wk->thread() << "==" << this->thread() << this->metaObject()->className();
        wk2->emitAfterWork();
    });
    //此处connect用于判断发送端的线程是:发送者的线程 还是 发送信号的线程
    //由于wk2与当前类都在主线程里面,而"上面的connect使用无接收者的connect函数,则信号会在子线程发出"
    //如果发送端的线程是指wk2所在的线程,那么下面connect无论是有接收者还是没有,那打印出来的线程应该都是一样的。
    //如果不一样,则说明线程由发送信号的线程决定而不是发送者所在的线程

    //无接收者connect打印结果:QThread(0x767720)  == QThread(0x7657d8) == QThread(0x7657d8) MainWindow
    //结果表明:在没有接收者的情况下,槽函数所在线程是由信号端确定的,但这里线程又不是发送者所在的线程,那么就是槽函数的线程就是由发送信号的线程所确定

    //有接收者connect打印结果:QThread(0x1316550)  == QThread(0x1316550) == QThread(0x1316550) MainWindow


    //总结,无论什么情况都应该尽量使用有接收者的connect,而无接收者的connect 大致等价与 有接收者的connect的第五个参数为Qt::DirectConnection。
    //即槽函数在发送信号的线程中执行,这样是不安全的。
    connect(wk2, &Worker2::afterWork, this, [ = ]()
    {
        myDebug << "收到下班信号,mainwindow";
        myDebug << QThread::currentThread() << " ==" << wk2->thread() << "==" << this->thread() << this->metaObject()->className();
    });

}

MainWindow::~MainWindow()
{
    delete ui;
}

结论

总结,无论什么情况都应该尽量使用有接收者的connect,而无接收者的connect 大致等价与 有接收者的connect的第五个参数为Qt::DirectConnection。 即槽函数在发送信号的线程中执行,这样是不安全的。

相关推荐
码哥DFS2 分钟前
构造函数、实例对象、对象原型 ----三者关系
开发语言·javascript·原型模式
quantdash_cc31 分钟前
告别自建 Requests/BS4 网页爬虫:基于 QuantDash 搭建零维保的高性能量化行情流水线
开发语言·爬虫·python·pandas·量化·quantdash
ydd10010038 分钟前
寻找两个正序数组的中位数 Java 题解,二分分割详解
java·开发语言
zh路西法1 小时前
【3D SLAM源码解读系列】(二)Small_gicp——5 个积木搭出最优点云配准
c++·pcl·icp·fastgicp·smallgicp·gicp
半亩码田1 小时前
C#转Python第3.1篇:Python 的 class 没有访问修饰符?面向对象的另一条路
开发语言·python·c#
Wzx1980122 小时前
python沙箱和docker沙箱你选对了吗?
开发语言·python·docker
牧羊人.3332 小时前
Python 办公自动化从入门到入土|09 数据容器之字典
开发语言·python
小僧景贤2 小时前
嵌入式C语言 第二篇:基础语法|嵌入式C与标准C的核心差异
c语言·开发语言·嵌入式c语言
阿pin2 小时前
Android随笔-AIDL
android·开发语言·aidl
fpcc3 小时前
ubuntu26环境下的开发环境安装处理
c++·并行编程