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
{
    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的连接方式就实现了信号和槽函数的同步执行。 

 

--

完整的源码如下:

头文件:

#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);
}


 

  • 3
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Qt中,线程信号连接需要使用Qt线程间通信机制。下面是一些基本的步骤: 1. 使用QObject::moveToThread()方法将对象移动到目标线程。 2. 在目标线程中创建一个QObject,并调用QObject::connect()方法将信号连接起来。 3. 如果信号需要传递参数,则需要使用Qt的元对象系统(Meta-Object System)来注册参数类型。可以使用Q_DECLARE_METATYPE宏来注册自定义类型。 4. 在发送信号时,需要使用QMetaObject::invokeMethod()方法,将该方法的第一个参数设置为接收信号的对象,第二个参数设置为接收信号的函数名,第三个参数设置为Qt::QueuedConnection,以确保信号被放入目标线程的事件队列中。 下面是一个简单的示例: ```cpp class Worker : public QObject { Q_OBJECT signals: void resultReady(int result); public slots: void doWork() { int result = 0; // 计算结果 emit resultReady(result); } }; class Controller : public QObject { Q_OBJECT public: Controller() { Worker *worker = new Worker(); QThread *workerThread = new QThread(); worker->moveToThread(workerThread); connect(workerThread, &QThread::started, worker, &Worker::doWork); connect(worker, &Worker::resultReady, this, &Controller::handleResult); workerThread->start(); } ~Controller() { // 停止线程 } public slots: void handleResult(int result) { // 处理结果 } }; ``` 在这个例子中,Worker对象的doWork()方法在一个新的线程中执行,当计算完成后,使用信号resultReady(int result)发送结果。Controller对象将自己的handleResult(int result)连接到该信号上,在目标线程中处理结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值