天天看點

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);//注意第五個參數

再次運作一下,檢視輸出:

這次隻發送了一次信号,但是咱們連接配接了5次,是以采用Qt::UniqueConnection方式連接配接,無論連接配接多少次,隻發送一次信号,也隻會執行一次槽函數

2.信号和槽函數在不同線程中的情況

自定義線程類:

#pragma once

#include <QThread>

class QtTestThread : public QThread {undefined

public:

   QtTestThread(QObject *parent);

   ~QtTestThread();

protected:

   void run();

   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;

   void slotThread();

private:

   QtTestThread* testThread;

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

繼續閱讀