Qt线程/进程间数据通讯
本文主要用于阐述说明Qt线程/进程间数据通讯相关内容
Qt线程间数据通讯
方法一 全局变量
优点:使用方便;
缺点:全局变量长时间占用内存,影响程序空间使用率,且全局变量修改影响整个程序,程序的安全性无法保证;
注意:使用时需根据实际状况,加入锁,防止多线程同时使用同一个变量导致异常;
方法二 信号槽
只有QObject类及其派生的类才能使用信号和槽的机制,在线程间使用信号槽进行通信时,槽函数必须使用元数据类型的参数;如果使用自定义的数据类型,需要在connect之前将其注册(qRegisterMetaType)为元数据类型。
下面例子为使用自定义的数据类型作为信号槽传递的参数,使用方法如下:
// mystruct.h
typedef struct MYSTRUCT{
unsigned char data1[4];
int data2;
double data3;
...
}MYSTRUCT;
------------------------------------------------------------------
//mainwindow.h
... //表示无关代码shenglve
#include "testthread.h"
#include "mystruct.h"
#include <QMetaType>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
void init();
public slots:
void slot_receive(const MYSTRUCT mystruct)
private:
TestThread *m_testThread;
...
}
------------------------------------------------------------------
//mainwindow.cpp
...
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
init();
}
void Mainwindow::init()
{
qRegisterMetaType<MYSTRUCT>("mystruct");
//MYSTRUCT 为自定义数据类型
//mystruct 为定义的名称
m_testThread = new TestThread();
connect(m_testThread, &TestThread::signal_send, this, &Mainwindow::slot_receive);
m_testThread->start();
}
void Mainwindow::slot_receive(const MYSTRUCT mystruct)
{
}
...
------------------------------------------------------------------
//testthread.h
#include "mystruct.h"
...
class TestThread : public QThread
{
Q_OBJECT
public:
TestThread();
virtual void run();
signals:
void signal_send(const MYSTRUCT mystruct);
private:
MYSTRUCT m_struct;
...
}
------------------------------------------------------------------
//testthread.cpp
...
TestThread::TestThread()
{
m_struct.data1[0] = 1;
m_struct.data1[1] = 1;
m_struct.data1[2] = 1;
m_struct.data1[3] = 1;
m_struct.data2 = 2;
m_struct.data3 = 3.0;
}
void run()
{
while(1)
{
emit signal_send(m_struct);
sleep(1000);
}
}
上述例程中线程使用 run() 方式运行,还可以使用 movetothread()(各路大佬推荐使用)方式运行;
线程间用信号槽传递参数的话,要加const,因为const文字常量存在常量区中,生命周期和程序一样长,可以避免slot调用的时候参数的运行期已过造成引用无效;
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通信相关内容。