多进程交互中,其中共享内存是比较常用的一种交互方式,比较高效且易于调试。网上虽然也有很多基于QSharedMemory的实现,但是都是比较基础的,同时读写,读完后分离进程之类的都没有完全保证安全性。所以我花了一整天重新封装了一个基于QSharedMemory的读写安全的类,包含支持传入成员回调函数,以及一次性压入共享内存中好几包的分包处理。此类继承于QThread,遍历收取消息,当遍历到新数据后,处理分包后,回调成员函数直接执行成员函数代码段。
sharedmemory.h
#ifndef SHAREDMEMORY_H
#define SHAREDMEMORY_H
#include <QThread>
#include <QSharedMemory>
#include <stdio.h>
#include <QSystemSemaphore>
#include <QBuffer>
#include <QDataStream>
#include <QDebug>
//创建一个基类,因为子类是模板类,所以后面调用运行时直接用基类的纯虚函数指向子类运行函数运行
class CallBack
{
public:
virtual ~CallBack(){}
virtual void execute(QByteArray) = 0;
};
//实现基类,方便后面绑定类与函数
template <typename T>
class ConcretaCallBack: public CallBack
{
public:
typedef void (T::*Method)(QByteArray);
ConcretaCallBack(T* instance,Method thod)
:m_instance(instance),m_thod(thod){}
void execute(QByteArray arrData)override{
(m_instance->*m_thod)(arrData);
}
private:
T* m_instance;
Method m_thod;
};
struct QueueHeader {
int itemCount; // 队列中项目的数量
int headOffset; // 队列头的偏移量
int tailOffset; // 队列尾的偏移量
// ... 可以添加更多控制信息,如锁状态等
};
struct QueueItem {
int dataSize; // 数据块的大小
// 数据块紧随其后
};
class SharedMemory : public QThread
{
public:
typedef void (*GenericMethodPtr)(QByteArray arrData);
explicit SharedMemory(const QString &key = nullptr);
~SharedMemory();
bool create(int size);
bool attach();
bool detach();
bool write(QByteArray data);
QList<QByteArray> readAll();
template<typename ClassType,typename Method>
void bind(ClassType* targetObject,Method memFunc)
{
if(m_callback != nullptr)
{
delete m_callback;
m_callback = nullptr;
}
m_callback = new ConcretaCallBack<ClassType>(targetObject,memFunc);
}
void run()override;
private:
QSharedMemory m_sharedMemory;
CallBack* m_callback = nullptr;
QSystemSemaphore semaphore; // 用于同步对共享内存的访问
};
#endif // SHAREDMEMORY_H
sharedmemory.cpp
#include "sharedmemory.h"
SharedMemory::SharedMemory(const QString &key)
: m_sharedMemory(key),
semaphore("semaphore_" + key,1)
{
}
SharedMemory::~SharedMemory()
{
if(m_callback != nullptr)
{
delete m_callback;
m_callback = nullptr;
}
}
bool SharedMemory::create(int size)
{
if(m_sharedMemory.isAttached())
{
m_sharedMemory.detach();
}
if(!m_sharedMemory.create(size))
{
return false;
}
return true;
}
bool SharedMemory::attach()
{
if(m_sharedMemory.isAttached())
{
return true;
}
return m_sharedMemory.attach();
}
bool SharedMemory::detach()
{
if(!m_sharedMemory.isAttached())
{
return true;
}
return m_sharedMemory.detach();
}
bool SharedMemory::write(QByteArray data)
{
semaphore.acquire(); // 请求信号量
if (!attach()) {
semaphore.release();
qDebug()<<"Error:sharedMemory(W) attach faild!";
return false;
}
if (!m_sharedMemory.lock()) {
// m_sharedMemory.detach();
semaphore.release();
qDebug()<<"Error:sharedMemory(W) lock faild!";
return false;
}
QueueHeader header;
memcpy(&header, m_sharedMemory.data(), sizeof(QueueHeader));
// 确认是否有足够的空间写入新的数据块
if (m_sharedMemory.size() - header.tailOffset < data.size() + sizeof(QueueItem)) {
m_sharedMemory.unlock();
// m_sharedMemory.detach();
semaphore.release();
qDebug()<<"Error:sharedMemory(W) is full!";
return false;
}
// 写入数据块
QueueItem item;
item.dataSize = data.size();
memcpy((char *)m_sharedMemory.data() + header.tailOffset, &item, sizeof(QueueItem));
memcpy((char *)m_sharedMemory.data() + header.tailOffset + sizeof(QueueItem), data.constData(), data.size());
// 更新队列头信息
header.itemCount += 1;
header.tailOffset += sizeof(QueueItem) + data.size();
memcpy(m_sharedMemory.data(), &header, sizeof(QueueHeader));
m_sharedMemory.unlock();
// m_sharedMemory.detach();
semaphore.release(); // 释放信号量
return true;
}
void SharedMemory::run()
{
while(true)
{
QThread::msleep(1000);
QByteArray frameData;
QList<QByteArray> result = readAll();
for (int i = 0;i<result.count();i++)
{
if(m_callback != nullptr)
{
m_callback->execute(result[i]);
}
}
}
}
QList<QByteArray> SharedMemory::readAll()
{
QList<QByteArray> dataQueue;
semaphore.acquire(); // 请求信号量
// if (!m_sharedMemory.isAttached()) {
if (!attach()){
semaphore.release();
return dataQueue;
}
if (!m_sharedMemory.lock()) {
m_sharedMemory.detach();
semaphore.release();
qDebug()<<"Error:sharedMemory(R) lock faild!";
return dataQueue;
}
QueueHeader header;
memcpy(&header, m_sharedMemory.data(), sizeof(QueueHeader));
// 读取队列中的所有数据
int offset = header.headOffset;
for (int i = 0; i < header.itemCount; ++i) {
QueueItem item;
memcpy(&item, (char*)m_sharedMemory.data() + offset, sizeof(QueueItem));
QByteArray data(item.dataSize, Qt::Uninitialized);
memcpy(data.data(), (char*)m_sharedMemory.data() + offset + sizeof(QueueItem), item.dataSize);
dataQueue.append(data);
offset += sizeof(QueueItem) + item.dataSize;
}
// 清空队列
header.itemCount = 0;
header.headOffset = sizeof(QueueHeader);
header.tailOffset = sizeof(QueueHeader);
memcpy(m_sharedMemory.data(), &header, sizeof(QueueHeader));
m_sharedMemory.unlock();
m_sharedMemory.detach();
semaphore.release(); // 释放信号量
return dataQueue;
}
发送线程调用规则:
SharedMemory* m_sharedMemory = new SharedMemory("Data");
//第一次进来先分离复位
m_sharedMemory->detach();
m_sharedMemory->create(65535*10);
m_sharedMemory->write(QByteArray("testSend"));
接收线程调用规则:文章来源:https://www.toymoban.com/news/detail-799431.html
//业务处理类
MainWindow mainW;
//共享指针初始化
SharedMemory sharedMem("Data");
//绑定处理函数
sharedMem.bind(&mainW,&MainWindow::DealMessage);
//线程遍历接收处理
sharedMem.start();
小彩蛋,子进程qDebug数据,主进程无法监视,这时候需要做个小小的处理,让所有输出输出到标准输出里面,这样子进程就能使用qDebug了文章来源地址https://www.toymoban.com/news/detail-799431.html
#include <stdio.h>
#include <iostream>
void myMessageOutput(QtMsgType type,const QMessageLogContext &context,const QString &msg)
{
Q_UNUSED(context)
QByteArray localMsg = msg.toLocal8Bit();
switch (type) {
case QtDebugMsg:
fprintf(stdout,"%s",localMsg.constData());
break;
case QtInfoMsg:
fprintf(stdout,"%s",localMsg.constData());
break;
case QtWarningMsg:
fprintf(stdout,"%s",localMsg.constData());
break;
case QtCriticalMsg:
fprintf(stdout,"%s",localMsg.constData());
break;
case QtFatalMsg:
fprintf(stdout,"%s",localMsg.constData());
abort();
}
fflush(stdout);
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
qInstallMessageHandler(myMessageOutput);
return a.exec();
}
到了这里,关于基于QSharedMemory的读写安全的共享内存的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!