Qt跨線程的信號和槽的使用


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 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM