Qt跨线程的信号和槽的使用 zz

https://blog.csdn.net/libaineu2004/article/details/86487354

connect用于连接qt的信号和槽,在qt编程过程中不可或缺。它其实有第五个参数,只是一般使用默认值,在满足某些特殊需求的时候可能需要手动设置。

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

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

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

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

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

QObject::connect()本身是线程安全的。槽函数一般是不安全的。

1.信号和槽函数在同一个线程中的情况
class Test: public QMainWindow
{
    Q_OBJECT
Test()
signals:
    void sigFirst();
private slots:
    void slotFirst();
}

Test::Test(QWidget *parent)
: QMainWindow(parent) {
    ui.setupUi(this);
    for (int i = 0; i < 5; i++) {//采用默认方式,连接5次
        connect(this, SIGNAL(sigFirst()), this, SLOT(slotFirst()));
    }

    emit sigFirst();
}


void Test::slotFirst() {
    numCoon++;
    qDebug() << QStringLiteral("信号第")<<numCoon<<QStringLiteral("次连接");
}

运行之后的输出内容:

"信号第" 1 "次连接"
"信号第" 2 "次连接"
"信号第" 3 "次连接"
"信号第" 4 "次连接"
"信号第" 5 "次连接"`

如果代码修改一下,改为:

connect(this, SIGNAL(sigFirst()), this, SLOT(slotFirst()), Qt::UniqueConnection);//注意第五个参数

再次运行一下,查看输出:

"信号第" 1 "次连接"

这次只发送了一次信号,但是咱们连接了5次,所以采用Qt::UniqueConnection方式连接,无论连接多少次,只发送一次信号,也只会执行一次槽函数

2.信号和槽函数在不同线程中的情况
自定义线程类:

#pragma once

#include <QThread>

class QtTestThread : public QThread {
    Q_OBJECT

public:
    QtTestThread(QObject *parent);
    ~QtTestThread();
protected:
    void run();
signals:
    void sigSecond();
};

#include "QtTestThread.h"
#include <QDebug>

QtTestThread::QtTestThread(QObject *parent)
: QThread(parent) {
}

QtTestThread::~QtTestThread() {
}

void QtTestThread::run() {
    emit sigSecond();
    qDebug() << QStringLiteral("信号发送完毕!");
}

调用线程类:

class QtTestThread;
class Test: public QMainWindow
{
    Q_OBJECT
Test()
signals:
    void sigFirst();
private slots:
    void slotThread();
private:
    QtTestThread* testThread;
}

#include "QtTestThread.h"
Test::Test(QWidget *parent)
: QMainWindow(parent) {
    ui.setupUi(this);

    testThread = new QtTestThread(this);
    connect(testThread, SIGNAL(sigSecond()), this, SLOT(slotThread()));//没有第五个参数,也就是采用默认的连接方式

    testThread->start();
}

void Test::slotThread() {
    qDebug() << QStringLiteral("线程发送的信号-槽函数执行!");
    QThread::sleep(3);
}

运行一下,输出内容:

"信号发送完毕!"
"线程发送的信号-槽函数执行!"

由此可以看出,信号发送完成信号后,就直接运行下面的代码了,而发送的信号就会被放到主线程的信号队列中等待执行。

咱们信号槽的连接方式修改一下,添加信号槽的连接方式 Qt::BlockingQueuedConnection:

connect(testThread, SIGNAL(sigSecond()), this, SLOT(slotThread()), Qt::BlockingQueuedConnection);

再次运行一下:

"线程发送的信号-槽函数执行!"
"信号发送完毕!"//时间等待3秒之后才输出这句话

采用Qt::BlockingQueuedConnection的连接方式就实现了信号和槽函数的同步执行。

 

--

完整的源码如下:

头文件:

复制代码
 1 #ifndef MAINWINDOW_H
 2 #define MAINWINDOW_H
 3  
 4 #include <QMainWindow>
 5 #include <QThread>
 6  
 7 namespace Ui {
 8 class MainWindow;
 9 }
10  
11 class QtTestThread : public QThread {
12     Q_OBJECT
13  
14 public:
15     QtTestThread(QObject *parent);
16     ~QtTestThread();
17  
18 protected:
19     void run();
20  
21 signals:
22     void sigSecond();
23 };
24  
25 class MainWindow : public QMainWindow
26 {
27     Q_OBJECT
28  
29 public:
30     explicit MainWindow(QWidget *parent = nullptr);
31     ~MainWindow();
32  
33 private:
34     Ui::MainWindow *ui;
35  
36 signals:
37     void sigFirst();
38  
39 private slots:
40     void slotFirst();
41     void slotThread();
42  
43 private:
44     QtTestThread* testThread;
45     int numCoon;
46 };
47  
48 #endif // MAINWINDOW_H
复制代码

源文件:

复制代码
 1 #include "mainwindow.h"
 2 #include "ui_mainwindow.h"
 3 #include <QDebug>
 4  
 5 QtTestThread::QtTestThread(QObject *parent)
 6     : QThread(parent)
 7 {
 8 }
 9  
10 QtTestThread::~QtTestThread()
11 {
12 }
13  
14 void QtTestThread::run()
15 {
16     emit sigSecond();
17     qDebug() << QStringLiteral("信号发送完毕!");
18 }
19  
20 MainWindow::MainWindow(QWidget *parent) :
21     QMainWindow(parent),
22     ui(new Ui::MainWindow)
23 {
24     ui->setupUi(this);
25  
26     for (int i = 0; i < 5; i++) {//采用默认方式,连接5次
27         //connect(this, SIGNAL(sigFirst()), this, SLOT(slotFirst()));//没有第五个参数,也就是采用默认的连接方式
28         connect(this, SIGNAL(sigFirst()), this, SLOT(slotFirst()), Qt::UniqueConnection);//注意第五个参数
29     }
30  
31     numCoon = 0;
32     //emit sigFirst();
33  
34     testThread = new QtTestThread(this);
35     //connect(testThread, SIGNAL(sigSecond()), this, SLOT(slotThread()));//没有第五个参数,也就是采用默认的连接方式
36     //connect(testThread, SIGNAL(sigSecond()), this, SLOT(slotThread()), Qt::QueuedConnection);//效果同上
37     connect(testThread, SIGNAL(sigSecond()), this, SLOT(slotThread()), Qt::BlockingQueuedConnection);//效果不同
38     testThread->start();
39 }
40  
41 MainWindow::~MainWindow()
42 {
43     delete ui;
44 }
45  
46 void MainWindow::slotFirst()
47 {
48     numCoon++;
49     qDebug() << QStringLiteral("信号第")<<numCoon<<QStringLiteral("次连接");
50 }
51  
52 void MainWindow::slotThread()
53 {
54     qDebug() << QStringLiteral("线程发送的信号-槽函数执行!");
55     QThread::sleep(3);
56 }
复制代码
posted on 2021-01-26 15:21  莫水千流  阅读(411)  评论(0编辑  收藏  举报