RTX 线程通信之——内存池

2023-05-16

文章目录

    • Memory Pool
      • 为什么需要内存池?
      • 什么是内存池?
      • RTX内存池API
    • 案例: 按键控制LED灯
      • 定义相关
      • 创建相关
      • 执行相关
      • 实验效果
    • 小结
    • 参考资料

Memory Pool

内存池 (Memory Pool) 同消息队列一样,可以通过它实现线程间的数据传输

为什么需要内存池?

有人说,有了消息队列,咱还要设计一个内存池干嘛,吃饱了没事干啊~,别说,还真不是!

确实,我们可以设计消息队列(Message Queue)来进行线程间的大量数据传输,从而实现线程间通信(Inter-Thread-Communication)。但别忘了,消息队列存在以下缺点:

  • 开销大。每次通信都需要在消息队列中频繁移动数据,即FIFO式的移动
  • 消息队列只能传输整型或指针类型的数据,灵活性不高

针对上述问题,一种解决方案是创建一块静态内存池 (也就是共享内存池),用来存放传输的数据,然后将该内存池的地址通过消息队列传输,从而实现“零移动”的数据传输。换句话说,数据本身没有移动,移动的是数据对象的地址。内存池的优点如下:

  • 开销小。不需要移动数据本身,只需要移动数据对象地址,便可实现线程通信。
  • 内存池可以存放复杂类型的对象,灵活度较高

综上,内存池很好地弥补了消息队列的不足。当然,要实现数据通信,一般还得【内存池+消息队列】。为什么是一般呢?
有什么场合是只能用消息队列的吗?别说,还真有,使用内存池的一大前提是两个线程之间有共享内存,换句话说就是有相同的地址空间。如果没有怎么办?比如临界区,一次只能允许一个线程访问,这个时候内存池不能用了,而消息队列就可以派上用场了~

什么是内存池?

啥是内存池?咱先来看看官方对内存池的介绍:

Memory Pools are fixed-size blocks of memory that are thread-safe. They operate much faster than the dynamically allocated heap and do not suffer from fragmentation. Being thread-safe, they can be accessed from threads and ISRs alike. A Memory Pool can be seen as a linked list of available (unused) memory blocks of fixed and equal size. Allocating memory from a pool (using osMemoryPoolAlloc) simply unchains a block from the list and hands over control to the user. Freeing memory to the pool (using osMemoryPoolFree) simply rechains the block into the list.

在这里插入图片描述

抓重点,内存池有以下特点:

  • 内存池由一系列固定大小未使用的内存块(memory blocks)组成,可以看成一个动态链表,结点就是内存块。之所以是动态的,是因为在分配和释放的过程中该链表的长度会动态变化。
  • 内存池是线程安全的,其操作速度也比通常的动态内存分配(堆区分配)更快,也不会造成内存碎片。说它是线程安全的,指的是其可以正常被多个线程或ISR访问,而不会出现错误行为。

共享内存是线程之间数据通信的一种基本模型,而内存池正是采用了这一思想,即获得地址即可访问数据!

RTX内存池API

要使用内存池,得先知道RTX为用户提供的相关定义和函数接口,下面作简要介绍~

类型

  • osMemoryPoolAttr_t : 内存池属性结构体
  • osMemoryPoolId_t : 内存池句柄

函数

在这里插入图片描述

  • osMemoryPoolNew
osMemoryPoolId_t  osMemoryPoolNew(uint32_t block_count, uint32_t block_size,const osMemoryPoolAttr_t* attr)
// 输入
* block_count : 内存池中内存块的最大个数
* block_size : 每个内存块的字节数
* attr : 内存池属性,默认为NULL
// 输出
* 内存池ID
// 注意
* 不能在ISR中调用该函数
  • osMemoryPoolGetName
const char* osMemoryPoolGetName(osMemoryPoolId_t mp_id)
// 输入
* mp_id : 内存池ID
// 输出
* 内存池名字

osMemoryPoolAlloc

void* osMemoryPoolAlloc(osMemoryPoolId_t mp_id,uint32_t timeout)
// 输入
* mp_id: 内存池ID
* timeout: 超时设置
// 输出
* 分配的内存块地址或者NULL

osMemoryPoolFree

osStatus_t osMemoryPoolFree(osMemoryPoolId_t mp_id,void* block)
// 输入
* mp_id : 内存池ID
* block : 待释放的内存块地址c
// 输出
* 函数执行后的状态码: osOK、osErrorParameter、osErrorResource

osMemoryPoolGetCapacity

uint32_t osMemoryPoolGetCapacity(osMemoryPoolId_t mp_id)
// 输入
* mp_id : 内存池ID
// 输出
* 内存池允许的最大内存块数量

osMemoryPoolGetBlockSize

uint32_t osMemoryPoolGetBlockSize(osMemoryPoolId_t mp_id)
// 输入
* mp_id : 内存池ID
// 输出
* 内存块的大小

osMemoryPoolGetCount

uint32_t osMemoryPoolGetCount(osMemoryPoolId_t mp_id)
// 输入
* mp_id : 内存池ID
// 输出
* 内存池中已经被使用的内存块个数

osMemoryPoolGetSpace

uint32_t osMemoryPoolGetSpace(osMemoryPoolId_t mp_id)
// 输入
* mp_id : 内存池ID
// 输出
* 内存池中可用的内存块个数

osMemoryPoolDelete

osStatus_t osMemoryPoolDelete(osMemoryPoolId_t mp_id)
// 输入
* mp_id :内存池ID
// 输出
* 函数执行后的状态码 
// 注意
* 不能在ISR中调用该函数

案例: 按键控制LED灯

案例是最好的学习方式,这里笔者介绍一个玩具案例,具体步骤如下。

功能:实现两个线程,一个线程用来不断读取按键的状态,并将按键的状态封装到一个结构体中,然后通过内存池和消息队列将该数据发送给另一个线程;另一个线程负责接收该数据,并根据该数据来点亮对应的LED灯。

* KEY1 键按下后松开, 红灯闪烁。
* KEY2 键按下后松开, 绿灯闪烁。

定义相关

根据功能,我们需要定义两个线程,相应地有两个线程函数;还有一个内存池,包括相应的内存块数据结构体;最后还需要定义一个消息队列。这里需要说明一下通常我们是将内存池消息队列结合起来使用。内存池负责保存复杂对象本身,然后通过将该对象的地址传入消息队列,实现线程间通信。

// 定义两个线程
osThreadId_t led;   // LED灯
osThreadId_t key;   // 按键
void thread_led(void* arg);   // LED灯线程函数
void thread_key(void* arg);   // 按键线程函数

osMemoryPoolId_t mp_id;    // 内存池
typedef struct {           // 内存块数据结构体
	 uint8_t red;
	 uint8_t green;
}memory_block_t;

osMessageQueueId_t mq_id;  // 消息队列

创建相关

我们在主线程app_main的线程函数中创建我们的线程,内存池,和消息队列,并执行一些按键和GPIO的初始化函数。

void app_main (void *arg) {
	// 外设初始化
    LED_GPIO_Config();
	Key_GPIO_Config();
	
	// 内存池 + 消息队列
	mp_id = osMemoryPoolNew(16,sizeof(memory_block_t),NULL);      // 内存池负责保存对象
	mq_id = osMessageQueueNew(16,sizeof(memory_block_t*),NULL);   // 消息队列负责传递对象的地址
	
	// 两个线程
	key = osThreadNew(thread_key,NULL,NULL);
    led = osThreadNew(thread_led,NULL,NULL);
	
	osThreadExit();  // 任务完成,退出
}

执行相关

我们的执行函数,当然是两个线程函数了。首先我们来看管理按键的线程函数。

void thread_key(void* arg)
{
	memory_block_t*  mb_led;   // 内存块指针
	while(1){
		// 请求一个内存块
		mb_led = (memory_block_t*)osMemoryPoolAlloc(mp_id,osWaitForever);
		
		// 扫描按键,按键按下返回 1 ,否则0
		if( Key_Scan(GPIOA,GPIO_Pin_0) == KEY_ON)
			mb_led->red = 0;    // 红灯亮
		else
			mb_led->red = 1;    // 红灯灭 
		if(Key_Scan(GPIOC,GPIO_Pin_13) == KEY_ON)
			mb_led->green = 0;  // 绿灯亮
		else
			mb_led->green = 1;  // 绿灯灭
	
		// 将数据传入消息队列
		osMessageQueuePut(mq_id,&mb_led,NULL,osWaitForever);
		osDelay(100);
	}
}

然后,再来看管理LED灯的线程函数。

void thread_led(void* arg)
{
	memory_block_t* mb_led;  // 内存块指针
	while(1){
		osMessageQueueGet(mq_id,&mb_led,NULL,osWaitForever);
		LED1(mb_led->red);
		LED2(mb_led->green);
		// 完成一次传输,释放该内存块
		osMemoryPoolFree(mp_id,mb_led);
	}
}

具体的GPIO,按键配置啥的,就不讲了,那不是本文的范畴,你们没问题的~

实验效果

由于按下按键,松开后LED灯才会亮,所以为了抓拍这个实验现象,我拍了几十张,才成功~

KEY1键按下:

红灯亮

KEY2键按下:

在这里插入图片描述

小结

要实现线程之间的数据传输,非内存池+消息队列莫属了,话说这两个结合在一起是真的好用。任何复杂数据结构都可以封装成结构体,并将其保存在内存池中。需要传输该复杂对象吗?完全不需要!咱不传递该对象本身,我们通过消息队列来传递该对象的地址,有了地址就有了数据本身。传输一个地址,相对传输对象本身,开销还是挺小的。

当然了,本文只是简单介绍了内存池+消息队列的使用,并实现了一个玩具案例。更多的细节啊啥的,需要参考官方资料,以及实际的项目需求,希望对大家有所帮助,任何疑问,欢迎留言,谢谢~

参考资料

☞官方资料

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

RTX 线程通信之——内存池 的相关文章

  • 二叉树高度的三种计算方法

    计算二叉树的高度可以采用几种不同的算法 算法一 xff1a 采用后序遍历二叉树 xff0c 结点最大栈长即为二叉树的高度 xff1b 算法二 xff1a 层次遍历二叉树 xff0c 最大层次即为二叉树的高度 xff1b 算法三 xff1a
  • (C/C++)区间调度问题的解决及输出:动态规划、贪心算法(递归、递推)

    给定n个活动 xff0c 其中的每个活动ai包含一个起始时间si与结束时间fi 设计与实现算法从n个活动中找出一个最大的相互兼容的活动子集S 要求 xff1a 分别设计动态规划与贪心算法求解该问题 其中 xff0c 对贪心算法分别给出递归与
  • Ubuntu18.04 xrdp多用户远程桌面

    span class token function wget span http www c nergy be downloads install xrdp 3 0 zip unzip install xrdp 3 0 zip span c
  • char 与 unsign char的转换

    char为字符类型 unsigned char为无符号字符类型 二者均是占一个字节 xff0c 可以直接通过强制转换的方式 xff0c 把char类型的值转为unsigned char 当定义变量 xff1a unsigned char a
  • NIPS20 - 将对比学习用于监督学习任务《Supervised Contrastive Learning》

    文章目录 原文地址论文阅读方法初识相知回顾代码 原文地址 原文 论文阅读方法 三遍论文法 初识 对比学习这两年在自监督学习 无监督学习任务中非常火 xff0c 取得了非常优秀的性能 这个工作就是想办法将其用在监督学习任务中 xff0c 在训
  • ViewBinding的用法

    1 启用Viewbinding功能 xff1a 在模块build gradle文件android节点下添加如下代码 android span class token punctuation span span class token pun
  • android技巧:快速重建system.img

    修改android的系统模块后 xff0c 需要重新生成system img xff0c 以前我用make systemimage xff0c 但这个过程太慢了 xff0c 很烦人 于是通过make输出的信息 xff0c 找到一个比较快的方
  • 前端参数无法传递给后端,一直返回400

    写在前面 太久没写SpringBoot的代码了 xff0c 最近一直在刷题和复习基础 IO xff0c 集合 xff0c 反射 xff0c MySQL等 但是 xff0c 前两天突发奇想 xff0c 想做一个关于某K歌平台歌曲免费导出的网站
  • 如何彻底删除小黑记事本弹窗

    No1 正常步骤 在桌面右键单击这个记事本的图标 xff0c 然后在菜单中找到并点击 打开文件位置 这一项 xff08 在开始菜单搜索也可以 xff09 按照正常方法 xff0c 在控制面板中卸载该软件 在第一步打开的文件位置中 xff0c
  • thinkphp5 select对象怎么转数组?

    DB操作返回是数组 模型直接操作返回是对象 对象类型转换数组 打开 database php 增加或修改参数 39 resultset type 39 61 gt 39 think Collection 39 即可连贯操作 model 39
  • thinkphp select 返回数组

  • python爬虫beautifulsoup详细教程

    BeautifulSoup4是爬虫必学的技能 BeautifulSoup最主要的功能是从网页抓取数据 xff0c Beautiful Soup自动将输入文档转换为Unicode编码 xff0c 输出文档转换为utf 8编码 Beautifu
  • 程序员读书顺序!

    程序猿的读书历程 xff1a xx语言入门 gt xx语言应用实践 gt xxx语言高阶编程 gt xxx语言的科学与艺术 gt 编程之美 gt 编程之道 gt 编程之禅 gt 颈椎病康复指南
  • Windows重建EFI引导启动分区(esp分区)

    Windows重建EFI引导启动分区 xff08 esp分区 xff09 遇到这种情况windows已经无法启动 xff0c 所以需要u盘启动盘进入winPE系统 1 如果只是格式化了esp分区 xff0c 没有改变esp分区的类型 xff
  • 智能革命和未来社会《智能时代--大数据和智能革命重新定义未来》

    通过区块链 xff08 Block Chain xff09 在未来跟踪每一件商品从制造出来到被消费的完整行踪 比特币在一定程度上起到货币的作用 xff0c 并且成为全球很安全的洗钱工具 xff0c 源于它背后的一个技术 区块链 block即
  • cas 单点登陆实战-sso-config篇(五)

    本篇我们讲解cas单点登陆在与shiro集成 xff0c 在与redis集成遇到的问题 先看完整代码吧 package com madnet config import com google common base CaseFormat i
  • cas 登陆时验证信息无效

    1 先查看缓存中的地址等是否正确 xff0c 不正确清除缓存重新打包 2 检查证书是否正确
  • crontab任务堆积导致启动大量进程的处理(flock)

    crontab在执行任务过程中 xff0c 可能由于任务执行错误或者在下一个任务开始前 xff0c 前一个任务没有执行完成 xff0c 导致大量的进程产生 xff0c 最后导致死机等 可以使用flock来加锁防止启动大量进程 xff1a f
  • Ue4行为树学习笔记 二

    Ue4行为树学习笔记 二 前言引用资料 行为树创建于布局任务设立 追逐随机巡逻AI控制器的设置最终设置尾声 前言 因为篇幅原因 xff0c 没看过上篇还请移步至 Ue4行为树学习笔记 一 引用资料 行为树快速入门指南 UE4学习笔记 xff
  • Python实现人脸识别

    文章目录 前言一 face recognition的安装1 安装dlib1 安装face recognition 二 上源码总结 前言 face recognition face recognition中文使用说明 号称是世界上最简单的开源

随机推荐

  • GPU, CUDA,cuDNN三者的关系总结

    GPU CUDA cuDNN三者的关系总结 CPU 和 GPU cpu和gpu结构展示 GPU xff08 图像处理器 xff0c Graphics Processing Unit xff09 和CPU xff08 中央处理器 xff0c
  • android应用内多进程的实现

    Android应用内多进程的介绍 正常情况下 xff0c 一个apk启动后只会运行在一个进程中 xff0c 其进程名为AndroidManifest xml文件中指定的应用包名 xff0c 所有的基本组件都会在这个进程中运行 但是如果需要将
  • 只需两步获取任何小程序源码

    前言 xff1a 这个博文的转载挺多的 xff0c 不过原文在更新 xff0c qwerty472123大神的脚本也在更新 xff0c 好多文章转载后就不再更新了 xff0c 看以前的步骤操作 xff0c 可能会引起奇怪的bug 这种方法
  • linux环境变量、交叉编译工具链gcc

    linux环境变量 1 linux环境变量 Linux 是一个多用户操作系统 xff0c 每个用户都有自己专有的运行环境 用户所使用的环境由一系列变量所定义 xff0c 这些变量被称为环境变量 系统环境变量通常都是大写 每个用户都可以根据需
  • Ubuntu解决登录界面无限循环

    由于配置scala和spark xff0c 修改了 etc profile文件 xff0c 导致Ubuntu登录界面无线循环 解决方法 xff1a 将profile修改正确 这是我的正确的文件内容 xff0c 供参考 span class
  • 如何优化Win11右键菜单

    Win 11 微软已经在10月5日发布了Windows 11正式版 xff0c 很多朋友也已经升级了 不过对于Win11的一些新设计 xff0c 并不是所有人都能适应的 xff0c 例如新的右键快捷菜单 xff0c 就不少朋友表示接受不了
  • 简单消费者组件的抽象思考(C++11)

    异步输出日志 平时开发过程中总是不可避免会用到生产者 消费者模型来实现一些具体的功能 比如在应用程序中 xff0c 我们希望在关键的代码附近输出一些日志 到文件 xff0c 以备程序运行出现bug时尽可能地知道更多的运行时信息 xff0c
  • 利用Cloudflare API批量添加域名至Cloudflare账户

    在Cloudflare中其实是有提供API的 xff0c 可以利用其功能进行批量添加域名 xff0c 在这篇文章中 xff0c 我将尝试看看如何解决这个问题 第一 准备工作 1 拥有一个Cloudflare账户 2 需要执行脚本的Linux
  • 如何用vnc远程连接mac系统

    在mac os系统安装好后 xff0c 怎么用vnc来进行远程连接呢 xff0c 步骤在下方 xff1a 1 打开系统偏好设置 2 点击共享 3 勾选屏幕共享 选择允许访问的用户 点击电脑设置 4 填好vnc连接的密码 5 用vnc登录 x
  • 学生时代的书单

    大话系列的书 xff0c 用独特的行文风格 xff0c 以风趣 幽默的语言向读者讲述 概念原理知识 xff0c 用漫画式的插图帮助读者理解晦涩 枯燥的技术 xff0c 让我们在快乐中掌握知识 xff01 1 大话通信 通信基础知识读本 作者
  • NLTK语料库nltk.download()安装失败及下载很慢的解决方法

    一 解决nltk download 安装失败 import nltk nltk download 下载nltk语料库出现getaddrinfo failed 如下错误 xff1a 这里只需将Server Index路径改成NLTK官网htt
  • windows虚拟机ping不通解决方法

    防火墙在作祟 简单暴力方法一 xff1a 直接关了防火墙 安全操作方法二 xff1a 打开入站规则就OK 如果是端口不通 xff0c 新建个端口规则解决啦
  • Spring框架(一)---------基本配置

    一 spring框架的配置一般姿势 1 导包 2 创建对象 3 配置文件 xff08 src applicationContext xml xff09 3 1 导入约束 3 2 配置applicationContext xml文件 3 3测
  • windows下面notepad++编写的文件未保存,电脑死机,文件找回

    notepad恢复未保存的文件 xff0c 备份文件 C Users 你当前用户的用户名 AppData Roaming Notepad 43 43 backup可以恢复 如果找不到此文件 因为文件被隐藏了 xff0c 打开隐藏文件即可 抖
  • idea自带的Maven添加阿里镜像

    打开idea xff0c 并打开设置 在搜索框查找Maven xff0c 可以看到idea使用的Maven路径 xff0c 配置文件路径 xff0c 以及仓库路径 重点是看配置文件 xff1a settings xml 如果在配置文件路径下
  • Ubuntu16.04开机失败—进入tty1终端修复

    Ubuntu16 04开机失败 进入tty1终端修复 如图所示 xff0c 我的Ubuntu16 04开机的时候报错 xff0c 提示 etc profile 文件的第34行出错 我想起了这个是自己安装mysql和sqoop的时候配的路径
  • 下载网页中的视频的两种方法

    方法一 xff1a 使用360或者IE浏览器 1 进入播放视频的网页 xff0c 播放视频并缓冲完全 xff1b 2 点击浏览器 工具 栏菜单中 Internet 选项 xff1b 3 在弹出的窗口中间部位找到 设置 xff1b 4 在新窗
  • tigerVNC的简单使用教程(CentOS的远程桌面连接)

    tigerVNC的简单使用教程 xff08 CentOS的远程桌面连接 xff09 DayDreamingBoy的博客 CSDN博客 tigervnc 1 环境和软件准备 1 CentOS 6 3下 root 64 localhost rp
  • gazebo视角调整

    看见上图中的橘黄色的图标了吗 xff1f 点击下拉框 xff0c 就可以调整自己的视角 xff0c 然后配合Ctrl 43 鼠标拖拽就OK了 参考博客 参考一
  • RTX 线程通信之——内存池

    文章目录 Memory Pool为什么需要内存池 xff1f 什么是内存池 xff1f RTX内存池API 案例 xff1a 按键控制LED灯定义相关创建相关执行相关实验效果 小结参考资料 Memory Pool 内存池 Memory Po