前言:

众所周知,信号和槽机制是Qt的核心和特有的机制。

通常我们这样用:

connect(scrollBar, SIGNAL(valueChanged(int)), label,  SLOT(setNum(int)));

常用基础用法如果不太清楚,请自行百度,不是本文讲解的重点。

相信大家或多或少的都听过,这个函数是有第5个参数的,Qt帮助文档原型:

[static] QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)

建议大家:Qt帮助文档是最好的学习资料,一定要重视。

我们今天要讲解的重点就是第5个参数:连接类型。

Connect的第五个参数:连接Type

This enum describes the types of connection that can be used between signals and slots. In particular, it determines whether a particular signal is delivered to a slot immediately or queued for delivery at a later time.

翻译:
这个枚举描述了在信号和槽之间的连接类型。特定的,它决定了是否一个特定信号被输送到槽函数立即,还是在随后排队输送。
Qt::AutoConnection:
(Default) If the receiver lives in the thread that emits the signal, Qt::DirectConnection is used. Otherwise, Qt::QueuedConnection is used. The connection type is determined when the signal is emitted.

翻译:(默认)如果接收者生存于发送这个信号的线程,使用Qt::DirectConnection。否则,使用Qt::QueuedConnection。连接类型在信号发射的时候决定。
Qt::DirectConnection:
The slot is invoked immediately when the signal is emitted. The slot is executed in the signalling thread.
翻译:槽函数立即被调用当这个信号发出的时候。槽函数在发送信号的线程中执行。
Qt::QueuedConnection:
The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.
翻译:槽函数被调用当控制权返回到接收者线程的事件循环。槽函数在接受者的线程中执行。
Qt::BlockingQueuedConnection:
Same as Qt::QueuedConnection, except that the signalling thread blocks until the slot returns. This connection must not be used if the receiver lives in the signalling thread, or else the application will deadlock.
翻译:与Qt::QueuedConnection相同,除了发送信号的线程阻塞,直到槽函数返回。这种连接类型不能用于接收者生存于发送者线程,否则将造成死锁。
Qt::UniqueConnection:
This is a flag that can be combined with any one of the above connection types, using a bitwise OR. When Qt::UniqueConnection is set, QObject::connect() will fail if the connection already exists (i.e. if the same signal is already connected to the same slot for the same pair of objects). This flag was introduced in Qt 4.6.
翻译:这个标志可以和以上任意连接类型组合,用按位或(|)。当设置了Qt::UniqueConnection,QObject::connect()函数将失败如果这个连接已经存在(也就是说:相同的信号已经连接到相同的槽函数,而且是相同的一对对象)。This flag was introduced in Qt 4.6.
其他描述:
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. If you try to use a queued connection and get the error message:

  QObject::connect: Cannot queue arguments of type 'MyType'

Call qRegisterMetaType() to register the data type before you establish the connection.
When using signals and slots with multiple threads, see Signals and Slots Across Threads.
翻译:使用队列连接(Qt::QueuedConnection)时,参数必须是Qt元对象系统的类型,因为Qt需要拷贝这些参数,并且把他们保存在后台事件中。如果你尝试用队列连接(Qt::QueuedConnection),并且获得了错误信息:QObject::connect: Cannot queue arguments of type 'MyType'。
调用qRegisterMetaType()函数去注册这个数据类型在建立这个连接之前。

代码实践:

新建界面工程,继承自QMainWindow类

1.直接连接Qt::DirectConnection:

①同一线程:

mainwindow.cpp(主线程)

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QThread>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    connect(this,SIGNAL(sig()),this,SLOT(slot()),Qt::DirectConnection);

    emit sig();

    for(int i=0; i<10;i++)
    {
        qDebug()<<i;
    }
}

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

void MainWindow::slot()
{
    qDebug()<<"执行槽函数";
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "thread1.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;    

private slots:
    void slot();
signals:
    void sig();
};

#endif // MAINWINDOW_H

运行结果:

在这里插入图片描述
可以看到,emit发射信号后立马执行槽函数,没有任何等待。

②非同一线程

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    qDebug()<<u8"主线程ID"<<QThread::currentThreadId();

    connect(&m_thread1,SIGNAL(sigThread1()),this,SLOT(slot()),Qt::DirectConnection);

    m_thread1.start();
}

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

void MainWindow::slot()
{
    qDebug()<<u8"执行槽函数---线程ID"<<QThread::currentThreadId();
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <thread1.h>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

    Thread1 m_thread1;

private slots:
    void slot();

};

#endif // MAINWINDOW_H

thread1.cpp

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

Thread1::Thread1(QThread *parent)
    : QThread(parent)
{

}

void Thread1::run()
{
    qDebug()<<u8"Thread1线程ID"<<QThread::currentThreadId();

    emit sigThread1();

    for(int i=0;i<10;i++)
    {
        qDebug()<<i;
    }
}

thread1.h

#ifndef THREAD1_H
#define THREAD1_H

#include <QThread>

class Thread1 : public QThread
{
    Q_OBJECT
public:
    explicit Thread1(QThread *parent = 0);

protected:
    virtual void run();

signals:
    void sigThread1();
};

#endif // THREAD1_H

运行结果:

在这里插入图片描述
可以看出:emit发射信号后立马执行槽函数,没有任何等待;并且槽函数执行在Thread1线程中;

2.队列连接Qt::QueuedConnection:

①同一线程:

mainwindow.cpp:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QThread>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    connect(this,SIGNAL(sig()),this,SLOT(slot()),Qt::QueuedConnection);

    emit sig();

    for(int i=0; i<10;i++)
    {
        qDebug()<<i;
    }
}

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

void MainWindow::slot()
{
    qDebug()<<u8"执行槽函数";
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "thread1.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

private slots:
    void slot();
signals:
    void sig();
};

#endif // MAINWINDOW_H

运行结果:

在这里插入图片描述
可以看到:先执行完for循环,当空闲后再执行槽函数。

②.非同一线程:

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    qDebug()<<u8"主线程ID"<<QThread::currentThreadId();

    connect(&m_thread1,SIGNAL(sigThread1()),this,SLOT(slot()),Qt::QueuedConnection);

    m_thread1.start();
}

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

void MainWindow::slot()
{
    qDebug()<<u8"执行槽函数---线程ID"<<QThread::currentThreadId();
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <thread1.h>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;

    Thread1 m_thread1;

private slots:
    void slot();

};

#endif // MAINWINDOW_H

thread1.cpp

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

Thread1::Thread1(QThread *parent)
    : QThread(parent)
{

}

void Thread1::run()
{
    qDebug()<<u8"Thread1线程ID"<<QThread::currentThreadId();

    emit sigThread1();

    for(int i=0;i<10000;i++) //此处为10000次,加长时间,以便更清楚的观察现象
    {
        qDebug()<<i;
    }
}

thread1.h

#ifndef THREAD1_H
#define THREAD1_H

#include <QThread>

class Thread1 : public QThread
{
    Q_OBJECT
public:
    explicit Thread1(QThread *parent = 0);

protected:
    virtual void run();

signals:
    void sigThread1();
};

#endif // THREAD1_H

运行结果:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看出:thread1线程发送信号后,thread1接着做自己的事,主线程同样接着做自己的事。

当主线程空闲时,再执行槽函数,槽函数运行在主线程中。

3.阻塞队列连接:Qt::BlockingQueuedConnection:

代码参考上面的,将Qt::QueuedConnection改为Qt::BlockingQueuedConnection即可。

在这里插入图片描述
可以看到:规律同Qt::QueuedConnection,不过thread1线程发送完信号后,会阻塞,直到主线程的槽函数返回,thread1线程才会继续向下执行。

这就算讲完了吗?

在上面的多线程代码中,我们使用的方法是重写QThread的run()函数的方式。

thread1线程从run()函数开始,也从run()函数返回时退出,那么槽函数如何在thread1线程中执行那???

请继续学习Qt线程的第二种使用方法:moveToThread() (https://blog.csdn.net/QQ1402369668/article/details/87903305);

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐