Qt线程/进程间通讯

Qt线程/进程间数据通讯

1. Qt线程间通讯,主要应用场景是不同线程之间,传递数据,可使用全局变量,或者信号槽来进行数据的传递;
2. Qt进程间通讯,主要应用场景是使用Qt编写的程序之间相互通信;

Qt线程间数据通讯

方法一 全局变量

优点:使用方便;

缺点:全局变量长时间占用内存,影响程序空间使用率,且全局变量修改影响整个程序,程序的安全性无法保证;

注意:使用时需根据实际状况,加入,防止多线程同时使用同一个变量导致异常;

方法二 信号槽

只有QObject类及其派生的类才能使用信号和槽的机制,在线程间使用信号槽进行通信时,槽函数必须使用元数据类型的参数;如果使用自定义的数据类型,需要在connect之前将其注册(qRegisterMetaType)为元数据类型

下面例子为使用自定义的数据类型作为信号槽传递的参数,使用方法如下:

------------------------------------------------------------------
// thread1.h
#ifndef THREAD1_H
#define THREAD1_H

#include <QDebug>
#include <QObject>
#include <QThread>
#include <QTimer>
#include <QDateTime>

typedef struct MYSTRUCT
{
    double testData1;
    int testData2;
}MYSTRUCT;


class Thread1 : public QObject
{
    Q_OBJECT
public:
    Thread1(QObject *parent = nullptr);

signals:
    void workSignal();

public slots:
    void test1Slot(MYSTRUCT *data);
    void thread1And2TestSlot();

private slots:
    void timerTimeoutSlot();

private:
    QThread *m_pThread;
    QTimer *m_pTimer;
};

#endif // THREAD1_H


------------------------------------------------------------------
// thread1.cpp
#include "thread1.h"

Thread1::Thread1(QObject *parent) : QObject(parent)
{
    m_pThread = new QThread();
    this->moveToThread(m_pThread);

    m_pTimer = new QTimer();
    m_pTimer->setTimerType(Qt::PreciseTimer);
    connect(m_pTimer, &QTimer::timeout, this, &Thread1::timerTimeoutSlot);
    m_pTimer->moveToThread(m_pThread);

    m_pThread->start();
}

void Thread1::test1Slot(MYSTRUCT *data)
{
    qDebug()<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")<<__FUNCTION__<<QThread::currentThread();
    qDebug()<<"data->testData1: "<<data->testData1<<" ;   data->testData2: "<<data->testData2;
    m_pTimer->start(1000);
}

void Thread1::timerTimeoutSlot()
{
    qDebug()<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")<<__FUNCTION__<<QThread::currentThread();
    emit workSignal();
}

void Thread1::thread1And2TestSlot()
{
    qDebug()<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")<<__FUNCTION__<<QThread::currentThread();
}
------------------------------------------------------------------
------------------------------------------------------------------
// thread2.h
#ifndef THREAD2_H
#define THREAD2_H

#include <QDebug>
#include <QObject>
#include <QThread>
#include <QDateTime>


class Thread2 : public QObject
{
    Q_OBJECT
public:
    Thread2(QObject *parent = nullptr);

signals:
    void thread1And2TestSignal();

public slots:
    void test2Slot();

private:
    QThread *m_pThread;
};

#endif // THREAD2_H


------------------------------------------------------------------
// thread2.cpp
#include "thread2.h"

Thread2::Thread2(QObject *parent)
    : QObject(parent)
{
    m_pThread = new QThread();
    this->moveToThread(m_pThread);

    m_pThread->start();
}

void Thread2::test2Slot()
{
    qDebug()<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")<<__FUNCTION__<<QThread::currentThread();
    emit thread1And2TestSignal();
}
------------------------------------------------------------------
------------------------------------------------------------------
//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QDebug>
#include <QMainWindow>
#include <QDateTime>
#include <QEventLoop>
#include "thread1.h"
#include "thread2.h"


QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

signals:
    void test1Signal(MYSTRUCT *data);
    void test2Signal();

public slots:
    void workSlot();
    void testCompleteSlot();

private slots:
    void on_pushButton_clicked();

    void on_pushButton_2_clicked();

private:
    Ui::MainWindow *ui;

    Thread1 *m_pThread1;
    Thread2 *m_pThread2;
};
#endif // MAINWINDOW_H


------------------------------------------------------------------
//mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    qDebug()<<__FUNCTION__<<QThread::currentThread();

    m_pThread1 = new Thread1();
    qRegisterMetaType<MYSTRUCT>("MYSTRUCT");
    connect(this, &MainWindow::test1Signal, m_pThread1, &Thread1::test1Slot);
    connect(m_pThread1, &Thread1::workSignal, this, &MainWindow::workSlot);

    m_pThread2 = new Thread2();
    connect(this, &MainWindow::test2Signal, m_pThread2, &Thread2::test2Slot);

    connect(m_pThread2, &Thread2::thread1And2TestSignal, m_pThread1, &Thread1::thread1And2TestSlot);
}

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

void MainWindow::on_pushButton_clicked()
{
    qDebug()<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")<<__FUNCTION__<<QThread::currentThread();

    MYSTRUCT *pMyStruct = new MYSTRUCT;
    pMyStruct->testData1 = 3.14159;
    pMyStruct->testData2 = 100;

    emit test1Signal(pMyStruct);
}

void MainWindow::workSlot()
{
    qDebug()<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")<<__FUNCTION__<<QThread::currentThread();
}

void MainWindow::on_pushButton_2_clicked()
{
    qDebug()<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")<<__FUNCTION__<<QThread::currentThread();
    emit test2Signal();

}

void MainWindow::testCompleteSlot()
{
    qDebug()<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")<<__FUNCTION__<<QThread::currentThread();
}
------------------------------------------------------------------

代码详解

1.上述代码使用 **movetothread()**方式运行;
2.创建两个线程类 Thread1 和 Thread2,Thread1 用于展示主线程与次线程间的通讯,Thread2 用于展示线程间的通讯;
3.点击按钮1,执行按钮1槽函数 “on_pushButton_clicked()”,创建数据结构体实例,并填入具体数据,通过信号传递到 Thread1 中,Thread1 中对应槽函数输出具体数据,并启动定时器,定时向主线程发送信号,测试打印信息如下;

MainWindow QThread(0x1869600)
"2024-08-27 11:11:52" on_pushButton_clicked QThread(0x1869600)
"2024-08-27 11:11:52" test1Slot QThread(0x3359cc8)
data->testData1:  3.14159  ;   data->testData2:  100
"2024-08-27 11:11:53" timerTimeoutSlot QThread(0x3359cc8)
"2024-08-27 11:11:53" workSlot QThread(0x1869600)
"2024-08-27 11:11:54" timerTimeoutSlot QThread(0x3359cc8)
"2024-08-27 11:11:54" workSlot QThread(0x1869600)
4.点击按钮2,执行按钮2槽函数 "on_pushButton_2_clicked()",发送信号到 Thread2 ,Thread2执行对应槽函数 "test2Slot()",并发送信号 "thread1And2TestSignal()",Thread1 接收到信号,执行对应槽函数 "thread1And2TestSlot()",测试打印信息如下;
MainWindow QThread(0x19b9600)
"2024-08-27 11:22:52" on_pushButton_2_clicked QThread(0x19b9600)
"2024-08-27 11:22:52" test2Slot QThread(0x33e9b38)
"2024-08-27 11:22:52" thread1And2TestSlot QThread(0x33e9c18)

注意

1.在mainwindow.cpp构造函数中,连接 MainWindow 与 Thread1 间的信号槽前,需通过 qRegisterMetaType(“MYSTRUCT”) 注册自定义数据类型,这样才能通过信号槽传递该数据类型的变量;
2.Thread1 类中的定时器,需要在 MainWindow 中,通过信号槽,来间接启动;
3.注意线程间通过信号槽传递变量时,变量的生命周期。

*还可以使用 **run()*方式运行(不推荐,使用不方便,局限性大);

connect函数的第五个参数
(1) Qt::AutoConnection
如果发射信号的线程和执行槽函数的线程是同一个线程,此时等同于Qt::DirectConnection;如果不在同一个线程,就等同于Qt::QueuedConnection,是connect函数的默认参数;
(2) Qt::DirectConnection
发射信号和执行槽函数由同一个线程(信号发射的线程)完成,信号发射后槽函数立马执行,执行完毕后才继续执行“emit信号”后面的代码,即“emit信号”是阻塞的;
(3) Qt::QueuedConnection
发射信号的线程和执行槽函数的线程不是在同一个线程,此时发射信号的线程不阻塞,马上返回,当执行槽函数的线程被CPU调度时,就会执行槽函数;
(4) Qt::BlockingQueuedConnection
和Qt::QueuedConnection基本一样,只是发射信号的线程会被阻塞,直到槽函数被执行完毕,如果使用这个属性,需确保发射信号的线程与执行槽函数的线程不同,否则将发生死锁;
(5) Qt::UniqueConnection
唯一关联,该类型可以和上面的类型通过“|”符号配合使用,同一个信号与同一个槽只能调用connect一次,不能多次调用;

Qt进程间数据通讯

方法一 共享内存

原理:两个个进程共用同一片物理内存
实现方式如下:

进程A(数据写入)

//processA.h
#include <QSharedMemory>

class ProcessA
{
...
public:
	ProcessA();
	~ProcessA();
	void writeButtonClicked();
private:
	QSharedMemory *m_shareMemory;
...
}
...

---------------------------------------------------------

//processA.cpp
...
ProcessA::ProcessA()
{
...
	m_shareMemory = new QSharedMemory();    //实例化QSharedMemory类
	m_shareMemory->setKey("TestKey");     	//通过setKey()设置标签名称;
	//判断当前实例化对象m_shareMemory是否已经与进程连接,如果已经连接,使用函数detach()将与进程分离。
	if(m_shareMemory->isAttached())
	{       	
	    m_shareMemory->detach();
    }
    
	//使用函数create()创建共享内存段,判断是否创建成功,若失败,打印错误信息并返回
	if(!m_shareMemory->create(50))       	
	{
	   	qDebug() << m_shareMemory->errorString();
	   	return ;
	}
...
}

ProcessA::~ProcessA()
{
...
	//注意用完以后退出程序时在析构函数中释放
	m_shareMemory->detach();			
	delete m_shareMemory;
	m_shareMemory = nullptr;
...
}

void ProcessA::writeButtonClicked()
{
	char testStr[50] = "This is my share memory!";
	m_shareMemory->lock();                //使用共享内存前需调用lock()将共享内存上锁
	char *destination = reinterpret_cast<char *>(m_shareMemory->data());
	const char *source = testStr;
	memcpy(destination, source, 50);      //将数据写入共享内存
	m_shareMemory->unlock();              //调用unlock()函数解锁
}
...

进程B(读取写入)

//processB.h
#include <QSharedMemory>
class ProcessB
{
...
public:
	ProcessB();
	~ProcessB();
	void readButtonClicked();
private:
	QSharedMemory *m_shareMemory;
...
}
...

---------------------------------------------------------

//processB.cpp
...
ProcessB::ProcessB()
{
...
	m_shareMemory = new QSharedMemory();    //实例化QSharedMemory类
    m_shareMemory->setKey("TestKey");       //通过函数setKey()设置标签名称;
    //连接共享内存与进程
    if(!m_shareMemory->attach())          
    {
        qDebug() << "attach m_shareMemory error!";
        return ;
    }
...
}

ProcessB::~ProcessB()
{
...
	m_shareMemory->detach();
	delete m_shareMemory;
	m_shareMemory = nullptr;
...
}

void ProcessB::readButtonClicked()
{
    char testStr[50];
    m_shareMemory->lock();                //使用共享内存前需调用lock()将共享内存上锁
    const char *source = (char*)m_shareMemory->constData();
    char *destination = testStr;
    memcpy(destination, source, 50);      //从共享内存里读取数据
    m_shareMemory->unlock();              //调用unlock()函数解锁
}
...

方法二 socket通信

在两个进程之间创建socket,自己定义一套通信协议,通过socket,实现两个进程间的数据传输,在此不做赘述,可以参考socket通信相关内容。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值