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

简介: Qt跨线程的信号和槽的使用

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

{undefined

   Q_OBJECT

Test()

signals:

   void sigFirst();

private slots:

   void slotFirst();

}


Test::Test(QWidget *parent)

: QMainWindow(parent) {undefined

   ui.setupUi(this);

   for (int i = 0; i < 5; i++) {//采用默认方式,连接5次

       connect(this, SIGNAL(sigFirst()), this, SLOT(slotFirst()));

   }


   emit sigFirst();

}



void Test::slotFirst() {undefined

   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 {undefined

   Q_OBJECT


public:

   QtTestThread(QObject *parent);

   ~QtTestThread();

protected:

   void run();

signals:

   void sigSecond();

};


#include "QtTestThread.h"

#include <QDebug>


QtTestThread::QtTestThread(QObject *parent)

: QThread(parent) {undefined

}


QtTestThread::~QtTestThread() {undefined

}


void QtTestThread::run() {undefined

   emit sigSecond();

   qDebug() << QStringLiteral("信号发送完毕!");

}


调用线程类:


class QtTestThread;

class Test: public QMainWindow

{undefined

   Q_OBJECT

Test()

signals:

   void sigFirst();

private slots:

   void slotThread();

private:

   QtTestThread* testThread;

}


#include "QtTestThread.h"

Test::Test(QWidget *parent)

: QMainWindow(parent) {undefined

   ui.setupUi(this);


   testThread = new QtTestThread(this);

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


   testThread->start();

}


void Test::slotThread() {undefined

   qDebug() << QStringLiteral("线程发送的信号-槽函数执行!");

   QThread::sleep(3);

}


运行一下,输出内容:


"信号发送完毕!"

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


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


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


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


再次运行一下:


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

"信号发送完毕!"//时间等待3秒之后才输出这句话


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



--


完整的源码如下:


头文件:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QThread>
namespace Ui {
class MainWindow;
}
class QtTestThread : public QThread {
    Q_OBJECT
public:
    QtTestThread(QObject *parent);
    ~QtTestThread();
protected:
    void run();
signals:
    void sigSecond();
};
class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
private:
    Ui::MainWindow *ui;
signals:
    void sigFirst();
private slots:
    void slotFirst();
    void slotThread();
private:
    QtTestThread* testThread;
    int numCoon;
};
#endif // MAINWINDOW_H



源文件:


#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
QtTestThread::QtTestThread(QObject *parent)
    : QThread(parent)
{
}
QtTestThread::~QtTestThread()
{
}
void QtTestThread::run()
{
    emit sigSecond();
    qDebug() << QStringLiteral("信号发送完毕!");
}
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    for (int i = 0; i < 5; i++) {//采用默认方式,连接5次
        //connect(this, SIGNAL(sigFirst()), this, SLOT(slotFirst()));//没有第五个参数,也就是采用默认的连接方式
        connect(this, SIGNAL(sigFirst()), this, SLOT(slotFirst()), Qt::UniqueConnection);//注意第五个参数
    }
    numCoon = 0;
    //emit sigFirst();
    testThread = new QtTestThread(this);
    //connect(testThread, SIGNAL(sigSecond()), this, SLOT(slotThread()));//没有第五个参数,也就是采用默认的连接方式
    //connect(testThread, SIGNAL(sigSecond()), this, SLOT(slotThread()), Qt::QueuedConnection);//效果同上
    connect(testThread, SIGNAL(sigSecond()), this, SLOT(slotThread()), Qt::BlockingQueuedConnection);//效果不同
    testThread->start();
}
MainWindow::~MainWindow()
{
    delete ui;
}
void MainWindow::slotFirst()
{
    numCoon++;
    qDebug() << QStringLiteral("信号第")<<numCoon<<QStringLiteral("次连接");
}
void MainWindow::slotThread()
{
    qDebug() << QStringLiteral("线程发送的信号-槽函数执行!");
    QThread::sleep(3);
}


相关文章
|
2月前
|
编译器
(9)Qt中信号与槽重载的解决方案
本文介绍了在Qt中处理信号与槽重载问题的三种解决方案:使用函数指针、Qt提供的QOverload类和Qt4的宏方式。
127 3
|
7月前
|
安全 编译器 开发者
【Qt 学习笔记】Qt信号和槽的其他说明及Lambda表达式
【Qt 学习笔记】Qt信号和槽的其他说明及Lambda表达式
217 0
|
2月前
(8)Qt中的自定义信号
本文介绍了如何在Qt框架中创建和使用自定义信号,并通过一个父子窗口切换的示例来展示自定义信号的实现和应用。
83 3
(8)Qt中的自定义信号
|
27天前
|
C++
003 Qt_信号和槽-上
本文介绍了Qt中的信号与槽机制,包括信号和槽的概念、本质及连接方法,并演示了如何自定义槽函数。信号是事件的体现,槽是对信号的响应函数。通过信号与槽,可以将独立的控件关联起来,实现复杂的交互逻辑。文中还详细展示了如何在Qt项目中定义和使用槽函数,通过实例代码和图形化界面操作,帮助读者更好地理解和应用这一机制。
41 1
003 Qt_信号和槽-上
|
6月前
|
安全 C++ Windows
Qt信号与槽机制
Qt信号与槽机制
56 1
|
7月前
|
存储 安全 Java
Qt线程池+生产者消费者模型
Qt线程池+生产者消费者模型
287 5
|
3月前
|
网络协议 安全
QT多线程
本文详细介绍了在Qt中如何正确使用QThread以及信号槽跨线程的使用方式,包括线程的正确退出方法和QObject在不同线程中创建子对象时可能遇到的问题。同时,文章还提供了相关博客和资料的链接,用于进一步学习和参考。
|
3月前
|
数据库 数据库管理
qt对sqlite数据库多线程的操作
本文总结了在Qt中进行SQLite数据库多线程操作时应注意的四个关键问题,包括数据库驱动加载、加锁、数据库的打开与关闭,以及QsqlQuery变量的使用。
172 1
|
3月前
自己动手写QT多线程demo
本文是作者关于如何编写Qt多线程demo的教程,介绍了如何实现多线程功能,包括可暂停和继续的功能。文章提供了部分示例代码,展示了如何创建线程类、启动和管理线程,以及线程间的通信。同时,还提供了相关参考资料和免费下载链接。
|
4月前
|
程序员 C++
【Qt】信号与槽(下)
【Qt】信号与槽(下)