Qt/C++进程间通信:QSharedMemory 使用详解(附演示Demo)

news2025/1/17 23:23:29

在开发跨进程应用程序时,进程间通信(IPC)是一个关键问题。Qt 框架提供了多种 IPC 技术,其中 QSharedMemory 是一种高效的共享内存方式,可以实现多个进程之间快速交换数据。本文将详细讲解 QSharedMemory 的概念、用法及其主要函数的用途,帮助开发者更好地理解和使用它。


1. 什么是 QSharedMemory?

QSharedMemory 是 Qt 中用于进程间共享内存的类。它允许多个进程共享一块内存区域,从而避免数据传输时的 IO 操作,提高通信速度。通过共享内存,多个进程可以直接读写这块内存,而无需经过文件或网络传递。

QSharedMemory 的核心特点

  1. 唯一键(Key)标识

    • 每块共享内存通过唯一的键(字符串)标识。
    • 不同进程通过相同的键连接到共享内存。
  2. 线程安全性

    • 提供锁机制(lock()unlock())以保护共享内存的读写。
  3. 跨平台支持

    • Qt 的跨平台特性使 QSharedMemory 可以在不同操作系统上无缝使用。

2. QSharedMemory 的常用场景

  1. 实时数据共享

    • 如传感器数据、实时日志等需要在多个进程间快速传递。
  2. 高性能需求

    • 在频繁更新的大量数据(如图像处理、缓存共享)中,通过共享内存减少通信开销。
  3. 进程间消息传递

    • 两个或多个应用程序之间的简单数据交换。

3. QSharedMemory 的工作流程

共享内存的基本使用可以分为以下几个步骤:

  1. 创建共享内存

    • 第一个进程通过 create(size) 创建一块共享内存。
    • 分配的大小由数据的存储需求决定。
  2. 附加到共享内存

    • 其他进程通过 attach() 方法连接到已有的共享内存。
  3. 数据读写

    • 通过 lock()unlock() 保证线程安全,获取内存指针后读写数据。
  4. 释放共享内存

    • 调用 detach() 断开与共享内存的连接。

4. QSharedMemory 常用函数详解

以下是 QSharedMemory 类的常用函数及其作用:

函数名作用
构造函数创建 QSharedMemory 对象,指定唯一键标识共享内存。
create(size)创建指定大小的共享内存,如果共享内存已存在则返回失败。
attach()附加到已有的共享内存,连接成功后可以访问内存内容。
detach()断开与共享内存的连接,并释放资源(只有最后一个进程断开时共享内存才会被销毁)。
lock()锁定共享内存,防止其他进程或线程同时访问数据(用于数据同步)。
unlock()解锁共享内存,允许其他进程访问数据。
data() / constData()获取共享内存的指针,用于读写数据(data() 为可写指针,constData() 为只读指针)。
isAttached()检查当前进程是否已经连接到共享内存。
error() / errorString()获取最近一次操作的错误代码和描述,便于调试。

5. 使用示例:QSharedMemory 实现进程间通信

以下是一个完整的例子,展示如何通过 QSharedMemory 实现进程间的读写通信。

程序1:写入共享内存

程序1负责创建共享内存并向其中写入数据。

#include <QCoreApplication>
#include <QSharedMemory>
#include <QDebug>
#include <QTimer>
#include <QDateTime>

#define tc(a) QString::fromLocal8Bit(a)

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QSharedMemory sharedMemory("MySharedMemoryKey");

    // 创建共享内存,大小为 1024 字节
    if (!sharedMemory.create(1024)) {
        qDebug() << tc("无法创建共享内存:") << sharedMemory.errorString();
        return -1;
    }

    qDebug() << tc("共享内存已创建");

    // 定时写入动态数据
    QTimer timer;
    QObject::connect(&timer, &QTimer::timeout, [&]() {
        if (sharedMemory.lock()) {
            char *to = static_cast<char *>(sharedMemory.data());
            QString message = tc("程序1动态消息#") + QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
            QByteArray byteArray = message.toLocal8Bit();
            memcpy(to, byteArray.data(), byteArray.size() + 1); // 写入动态数据
            sharedMemory.unlock();
            qDebug() << tc("成功写入共享内存:") << message;
        } else {
            qDebug() << tc("无法锁定共享内存进行写入:") << sharedMemory.errorString();
        }
    });

    timer.start(1000); // 每秒更新一次

    return a.exec();
}

程序2:读取共享内存

程序2连接到共享内存,读取数据并解析时间戳。

#include <QCoreApplication>
#include <QSharedMemory>
#include <QDebug>
#include <QTimer>
#include <QDateTime>

#define tc(a) QString::fromLocal8Bit(a)

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QSharedMemory sharedMemory("MySharedMemoryKey");

    // 连接到已有的共享内存
    if (!sharedMemory.attach()) {
        qDebug() << tc("无法连接到共享内存:") << sharedMemory.errorString();
        return -1;
    }

    qDebug() << tc("成功连接到共享内存");

    // 定时读取数据
    QTimer timer;
    QObject::connect(&timer, &QTimer::timeout, [&]() {
        if (sharedMemory.lock()) {
            const char *from = static_cast<const char *>(sharedMemory.constData());
            QString data = QString::fromLocal8Bit(from);
            sharedMemory.unlock();

            // 解析时间戳
            QString timeStampString = data.split("#").at(1);
            QDateTime messageTime = QDateTime::fromString(timeStampString, "yyyy-MM-dd hh:mm:ss.zzz");

            // 当前时间
            QDateTime currentTime = QDateTime::currentDateTime();

            qDebug() << tc("从共享内存读取到的数据:") << data;

            // 如果时间戳解析成功,计算时间差
            if (messageTime.isValid()) {
                qint64 timeDifference = messageTime.msecsTo(currentTime); // 时间差(毫秒)
                qDebug() << tc("接收到的时间:") << messageTime.toString("yyyy-MM-dd hh:mm:ss.zzz");
                qDebug() << tc("当前时间:") << currentTime.toString("yyyy-MM-dd hh:mm:ss.zzz");
                qDebug() << tc("时间差(毫秒):") << timeDifference;
            } else {
                qDebug() << tc("无法解析时间戳!");
            }
        } else {
            qDebug() << tc("无法锁定共享内存进行读取:") << sharedMemory.errorString();
        }
    });

    timer.start(1000); // 每秒读取一次

    return a.exec();
}


6. 注意事项

  1. 共享内存大小

    • 创建共享内存时,指定的大小必须足够大以存储所有数据。
  2. 锁机制

    • 在操作共享内存前,必须调用 lock() 进行锁定,以避免数据竞争。
    • 使用完成后,必须调用 unlock() 解锁。
  3. 错误处理

    • 使用 error()errorString() 检查共享内存的状态。
  4. 进程退出

    • 调用 detach() 确保释放共享内存资源。

7. 总结

QSharedMemory 是一种高效的进程间通信方式,适用于需要快速传递数据的场景。通过本文的讲解,您应该能够掌握 QSharedMemory 的核心功能及其应用。无论是共享日志、实时数据,还是跨进程消息传递,QSharedMemory 都是一个值得考虑的解决方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2278197.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32的集成开发环境STM32CubeIDE安装

STM32CubeIDE - STM32的集成开发环境 - 意法半导体STMicroelectronics

NanoKVM简单开箱测评和拆解,让普通电脑实现BMC/IPMI远程管理功能

Sipeed推出了NanoKVM&#xff0c;简直是没有BMC的台式机和工作站的福音。有了这个就可以轻松实现以往服务器才有的远程管理功能。 NanoKVM 简介 Lichee NanoKVM 是基于 LicheeRV Nano 的 IP-KVM 产品&#xff0c;继承了 LicheeRV Nano 的极致体积 和 强大功能。 NanoKVM 包含…

唐刘:TiDB 的 2024 - Cloud、SaaS 与 AI

2024 年已经过去&#xff0c;在去年我也写过两篇类似的文章&#xff0c;TiDB in 2023 - 一次简单的回顾和 TiDB Cloud in 2023 - 一次简单的回顾&#xff0c;这一次&#xff0c;我准备将 TiDB 和 cloud 一起写&#xff0c;一方面原因是我懒了&#xff0c;另外一个更重要的原因在…

Web端实时播放RTSP视频流(监控)

一、安装ffmpeg: 1、官网下载FFmpeg: Download FFmpeg 2、点击Windows图标,选第一个:Windows builds from gyan.dev 3、跳转到下载页面: 4、下载后放到合适的位置,不用安装,解压即可: 5、配置path 复制解压后的\bin路径,配置环境变量如图: <

Excel 技巧10 - 如何检查输入重复数据(★★)

本文讲了如何在Excel中通过COUNTIF来检查输入重复数据。 当输入重复数据时&#xff0c;显示错误提示。 1&#xff0c;通过COUNTIF来检查输入重复数据 比如下面是想检查不要输入重复的学号。 选中C列&#xff0c;点 Menu > 数据 > 数据验证 在数据验证页面&#xff0c…

IEC103 转 ModbusTCP 网关

一、产品概述 IEC103 转 ModbusTCP 网关型号 SG-TCP-IEC103 &#xff0c;是三格电子推出的工业级网关&#xff08;以下简 称网关&#xff09;&#xff0c;主要用于 IEC103 数据采集、 DLT645-1997/2007 数据采集&#xff0c; IEC103 支持遥测和遥 信&#xff0c;可接…

Unity3d 实时天气系统基于UniStorm插件和xx天气API实现(含源码)

前言 实时天气在Unity3d三维数字沙盘中的作用非常重要&#xff0c;它能够增强虚拟环境的真实感和互动性&#xff0c;实时天气数据的应用可以提供更为精准和直观的天气信息支持&#xff0c;如果真实的数据加上特效、声音和模型反馈会提高产品档次&#xff0c;提高真实感。 目前…

Linux命令行工具-使用方法

参考资料 Linux网络命令&#xff1a;网络工具socat详解-CSDN博客 arm-linux-gnueabihf、aarch64-linux-gnu等ARM交叉编译GCC的区别_aarch64-elf-gcc aarch64-linux-gnu-CSDN博客 解决Linux内核问题实用技巧之-dev/mem的新玩法-腾讯云开发者社区-腾讯云 热爱学习地派大星-CS…

【Flink系列】9. Flink容错机制

9. 容错机制 在Flink中&#xff0c;有一套完整的容错机制来保证故障后的恢复&#xff0c;其中最重要的就是检查点。 9.1 检查点&#xff08;Checkpoint&#xff09; 9.1.1 检查点的保存 1&#xff09;周期性的触发保存 “随时存档”确实恢复起来方便&#xff0c;可是需要我…

【深度学习】关键技术-激活函数(Activation Functions)

激活函数&#xff08;Activation Functions&#xff09; 激活函数是神经网络的重要组成部分&#xff0c;它的作用是将神经元的输入信号映射到输出信号&#xff0c;同时引入非线性特性&#xff0c;使神经网络能够处理复杂问题。以下是常见激活函数的种类、公式、图形特点及其应…

20.<Spring图书管理系统①(登录+添加图书)>

PS&#xff1a;关于接口定义 接口定义&#xff0c;通常由服务器提供方来定义。 1.路径&#xff1a;自己定义 2.参数&#xff1a;根据需求考虑&#xff0c;我们这个接口功能完成需要哪些信息。 3.返回结果&#xff1a;考虑我们能为对方提供什么。站在对方角度考虑。 我们使用到的…

【Sql递归查询】Mysql、Oracle、SQL Server、PostgreSQL 实现递归查询的区别与案例(详解)

文章目录 Mysql 5.7 递归查询Mysql 8 实现递归查询Oracle递归示例SQL Server 递归查询示例PostgreSQL 递归查询示例 更多相关内容可查看 Mysql 5.7 递归查询 MySQL 5.7 本身不直接支持标准 SQL 中的递归查询语法&#xff08;如 WITH RECURSIVE 这种常见的递归查询方式&#xf…

vue2修改表单只提交被修改的数据的字段传给后端接口

效果&#xff1a; 步骤一、 vue2修改表单提交的时候&#xff0c;只将修改的数据的字段传给后端接口&#xff0c;没有修改得数据不传参给接口。 在 data 对象中添加一个新的属性&#xff0c;用于存储初始表单数据的副本&#xff0c;与当前表单数据进行比较&#xff0c;找出哪些…

「刘一哥GIS」系列专栏《GRASS GIS零基础入门实验教程(配套案例数据)》专栏上线了

「刘一哥GIS」系列专栏《GRASS GIS零基础入门实验教程》全新上线了&#xff0c;欢迎广大GISer朋友关注&#xff0c;一起探索GIS奥秘&#xff0c;分享GIS价值&#xff01; 本专栏以实战案例的形式&#xff0c;深入浅出地介绍了GRASS GIS的基本使用方法&#xff0c;用一个个实例讲…

纯命令 git使用

首先我们到一个新的公司 要添加一个新的git仓库的权限 我们应该现拉去代码 配置git的仓库信息 第一 先添加权限 第二 如果不是自己电脑 需要配置信息 配置基础信息 查看本地git账号git config --global user.name git config --global user.email修改本地账号git co…

Linux系统编程:深入理解计算机软硬件体系和架构

一、硬件体系 首先我们要知道&#xff0c;我们最常见的计算机&#xff08;笔记本&#xff09;以及我们不常见的计算机&#xff08;服务器&#xff09;其实本质上都是一堆硬件的结合&#xff1a;cpu、网卡、显卡、内存、磁盘、显示器、键盘…… 但他们并不是毫无章法地放在一起…

微信小程序-Docker+Nginx环境配置业务域名验证文件

在实际开发或运维工作中&#xff0c;我们时常需要在 Nginx 部署的服务器上提供一个特定的静态文件&#xff0c;用于域名验证或第三方平台验证。若此时使用 Docker 容器部署了 Nginx&#xff0c;就需要将该验证文件正确地映射&#xff08;挂载&#xff09;到容器中&#xff0c;并…

HarmonyOS NEXT应用开发边学边玩系列:从零实现一影视APP (五、电影详情页的设计实现)

在上一篇文章中&#xff0c;完成了电影列表页的开发。接下来&#xff0c;将进入电影详情页的设计实现阶段。这个页面将展示电影的详细信息&#xff0c;包括电影海报、评分、简介以及相关影人等。将使用 HarmonyOS 提供的常用组件&#xff0c;并结合第三方库 nutpi/axios 来实现…

在 macOS 上,用命令行连接 MySQL(/usr/local/mysql/bin/mysql -u root -p)

根据你提供的文件内容&#xff0c;MySQL 的安装路径是 /usr/local/mysql。要直接使用 mysql 命令&#xff0c;你需要找到 mysql 可执行文件的路径。 在 macOS 上&#xff0c;mysql 客户端通常位于 MySQL 安装目录的 bin 子目录中。因此&#xff0c;完整的路径应该是&#xff1…

使用docker-compose安装ELK(elasticsearch,logstash,kibana)并简单使用

首先服务器上需要安装docker已经docker-compose&#xff0c;如果没有&#xff0c;可以参考我之前写的文章进行安装。 https://blog.csdn.net/a_lllk/article/details/143382884?spm1001.2014.3001.5502 1.下载并启动elk容器 先创建一个网关&#xff0c;让所有的容器共用此网…