• Qt --- 信号槽:第五个参数


    connect第五个参数说明:

    1、Qt::AutoConnection: 默认值,使用这个值则连接类型会在信号发送时决定。如果接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型。如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型。

    2、Qt::DirectConnection:槽函数会在信号发送的时候直接被调用,槽函数运行于信号发送者所在线程。效果看上去就像是直接在信号发送位置调用了槽函数。这个在多线程环境下比较危险,可能会造成奔溃。

    3、Qt::QueuedConnection:槽函数在控制回到接收者所在线程的事件循环时被调用,槽函数运行于信号接收者所在线程。发送信号之后,槽函数不会立刻被调用,等到接收者的当前函数执行完,进入事件循环之后,槽函数才会被调用。多线程环境下一般用这个。

    4、Qt::BlockingQueuedConnection:槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。

    5、Qt::UniqueConnection:这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是避免了重复连接。

    使用Qt::QueuedConnection需要注意:对于信号槽的参数类型,是有限制的,必须得是 meta-object type.

    Qt文档中有指出:With queued connections, the parameters must be of types that are known to Qt's meta-object system, because Qt needs to copy the arguments to store them in an event behind the scenes.

    一。  遇到的问题:在GUI主线程向另一个线程发送信号,槽函数不执行。

     1 static EventLoop* gEventLoop = nullptr;
     2 
     3 void FFF()
     4 {
     5     gEventLoop = new EventLoop();
     6 }
     7 
     8 MainWindow::MainWindow(QWidget *parent) :
     9     QMainWindow(parent),
    10     ui(new Ui::MainWindow)
    11 {
    12     ui->setupUi(this);
    13     
    14     std::thread ttt(FFF); //另一个线程中创建对象
    15     ttt.join();
    16     
    17     connect(this, &MainWindow::Call, gEventLoop, &EventLoop::Called, Qt::QueuedConnection); //连接成功 槽函数不执行
    18     connect(gEventLoop, &EventLoop::eee, this, &MainWindow::HHH, Qt::QueuedConnection);
    19 
    20 }

    connect(this, &MainWindow::Call, eLoop, &EventLoop::Called, Qt::QueuedConnection); //连接成功 槽函数不执行

    这里因为发送信号的对象和接收信号的对象不在同一个线程,使用QueueConnection,但是发送信号后 槽函数不会执行。

    原因:

    信号发送成功后,会把信号放到接收对象所在线程的事件循环队列,排到它的时候,自动调用槽函数。

    但是std::thread创建的线程中,没有事件循环,所以没有接收到信号,槽函数不会执行。

    解决方法:

    1.为线程创建一个事件循环。(不会。。。)

    2.使用QThread开启线程,QThread自带默认事件循环。

    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
    
    //    std::thread ttt(FFF); //另一个线程中创建对象
    //    ttt.join();
    
    //    connect(this, &MainWindow::Call, eLoop, &EventLoop::Called, Qt::QueuedConnection); //连接成功 槽函数不执行
    //    connect(eLoop, &EventLoop::eee, this, &MainWindow::HHH, Qt::QueuedConnection);
    
        qThread = new QThread();
        eLoop = new EventLoop();
        eLoop->moveToThread(qThread);
    
        connect(qThread, &QThread::finished, qThread, &QObject::deleteLater);
        connect(qThread, &QThread::destroyed, [=](){qThread = nullptr;});
        connect(qThread, &QThread::finished, eLoop, &QObject::deleteLater);
        connect(eLoop, &QObject::destroyed, [=](){eLoop = nullptr;});
    
        connect(this, &MainWindow::Call, eLoop, &EventLoop::Called, Qt::QueuedConnection);
        connect(eLoop, &EventLoop::eee, this, &MainWindow::HHH, Qt::QueuedConnection);
    //    QObject::connect(eLoop, &EventLoop::eee, this, &MainWindow::HHH, Qt::QueuedConnection);
        qThread->start();
    
    }
    
    MainWindow::~MainWindow()
    { 
        qThread->exit();//结束线程(结束事件循环)
        qThread->wait();//阻塞 等待线程结束
        delete ui;
    }

    说明:

    qThread = new QThread();

    eLoop = new EventLoop(); //创建对象

    eLoop->moveToThread(qThread); //把对象放到线程中执行,此时线程自带事件循环,发送信号,槽函数能正确调用。(参考 https://www.huaweicloud.com/articles/2a9c964f7ee0900f9bd029224b9f2b26.html

    eLoop->moveToThread(qThread); //把对象放到线程中执行,此时线程自带事件循环,发送信号,槽函数能正确调用。

    更正:moveToThread() 函数的作用是将槽函数在指定的线程中被调用(调用指的是:发送信号后,槽函数会被放到指定线程的事件循环队列中等待执行。)。如果直接调用槽函数,那就是普通的函数调用过程。

    参考自:https://zhuanlan.zhihu.com/p/53270619

    小结:

    • 使用moveToThread这种QT多线程的方法,实现简单、使用灵活,并且思路清晰,相对继承于QThread类的方式更有可靠性,这种方法也是官方推荐的实现方法。如果线程要用到事件循环,使用继承QObject的多线程方法无疑是一个更好的选择;
    • 创建QObject派生类对象不能带有父类;
    • 把线程的finished信号和obj对象、QThread对象的 QObject::deleteLater 槽连接,这个信号槽必须连接,否则会内存泄漏;如果QObject的派生类和QThread类指针是需要重复使用,那么就需要处理由对象被销毁之前立即发出的 QObject::destroyed 信号,将两个指针设置为nullptr,避免出现野指针;
    • 调用QThread::start是默认启动事件循环;
    • 必须需要使用信号槽的方式使用线程;
    • 需要注意跨线资源的创建,例如QTimer、QUdpSocket等资源,如果需要在子线程中使用,必须要在子线程创建;
    • 要善用QObject::deleteLater 和 QObject::destroyed来进行内存管理 ;
    • 尽量避免使用terminate强制退出线程,若需要退出线程,可以使用quit或exit;

      追溯历史,在 Qt 4.4 版本以前的 QThread 类是个抽象类,要想编写多线程代码唯一的做法就是继承 QThread 类。但是之后的版本中,Qt 库完善了线程的亲和性以及信号槽机制,我们有了更为优雅的使用线程的方式,即 QObject::moveToThread()。这也是官方推荐的做法,遗憾的是网上大部分教程没有跟上技术的进步,依然采用 run() 这种腐朽的方式来编写多线程程序。-》https://zhuanlan.zhihu.com/p/53270619

    
    

    深入理解-》  Qt信号槽机制源码解析







  • 相关阅读:
    PAT 1020 月饼 (25)(精简版代码+思路+推荐测试用例)
    PAT 1010 一元多项式求导 (25)(STL-map+思路)
    通过无线连接的方式来做 Appium 自动化
    Eclipse shift + ctrl + F 不好用
    Appium 出现 > error: com.test/.activity1 never started. Current: com.test/.activity2
    Appium 出现 > error: com.test/.activity1 never started. Current: com.test/.activity2
    从CSDN 来到博客园入驻——2015/1/28
    敏捷自动化测试(1)—— 我们的测试为什么不够敏捷?
    敏捷自动化测试(2)——像用户使用软件一样享受自动化测试
    Android自动化测试之Monkey工具
  • 原文地址:https://www.cnblogs.com/linxisuo/p/13448239.html
Copyright © 2020-2023  润新知