首页 > 编程知识 正文

指定线程的内存,java线程内存工作区

时间:2023-05-04 20:16:34 阅读:163127 作者:2183

为什么文章目录Memory Pool需要内存池? 什么是内存池? RTX内存池API案例:按键控制LED灯定义相关制作相关实验效果总结参考资料

内存池

内存池(Memory Pool )提供线程之间的数据传输,就像消息队列一样。

为什么需要内存池? 有人说,有了消息队列,我们设计内存池做什么,吃饱了什么都不做~,恰恰相反,事实并非如此。

确实,我们可以设计消息队列来进行线程之间的大量数据传输,实现线程间通信。 但是,请不要忘记消息队列有以下缺点:

开支增多。 每次通信都需要在消息队列中频繁移动数据。 这意味着FIFO移动消息队列只能传输类型为整型或指针的数据。 针对上述问题,灵活性较低的一种解决方案是创建静态内存池 (也就是共享内存池)以存储传输的数据,然后在消息队列中传输其内存池地址,从而实现“零移动” 内存池的优点如下。

开销很少。 无需移动数据本身,只需移动数据对象地址就可以进行线程通信。 内存池可以存储复杂类型的对象,而且具有很大的灵活性,可以很好地弥补消息队列的不足。 当然,实现数据通信一般需要【内存池消息队列】。 为什么很普通呢?

有只能使用消息队列的场面吗? 当然,使用内存池的一个主要前提是两个线程之间有共享内存。 换句话说,就是有同样的地址空间。 如果不是呢? 例如,在关键节中,一次只能访问一个线程。 此时,内存池不可用,但消息队列很有用~

什么是内存池? 什么是内存池? 首先,让我们看一下官方内存池的介绍。

3358 www.Sina.com/http://www.Sina.com/are fixed-sizeblocksofmemorythatare 3358 www.Sina.com/. they operate 3358 thanthedynamicallyallocatedheapanddonotsufferfromfragmentation.being thread-safe, theycanbeaccessedfromthreadsandisrsalike.amemorypoolcanbeseenasalinkedlistofavailable (memoryblocksoffixedandequalsize.alsize ) simplyunchainsablockfromthelistandhandsovercontroltotheuser.freeingmemorytothepool (usingosmemorypoolfree ) simplyrechainsttstechainsttttor

重要的是,内存池具有以下特征:

内存池由一系列MemoryPools的内存块(memory blocks )组成,可视为动态链表,节点为内存块之所以是动态的,是因为在分配和释放过程中链表的长度会动态变化。 内存池为thread-safe,运行速度比常规动态内存分配(3358www.Sina.com/)快,而且不会出现内存碎片。 线程安全意味着可以由多个线程或ISR成功访问,不会导致错误行为。much faster是线程之间数据通信的基本模型,内存池考虑的是获得地址就可以访问数据。

RTX内存池要使API使用内存池,必须首先了解RTX为用户提供的相关定义和函数接口。 以下简单介绍~

固定大小

osMemoryPoolAttr_t :内存池属性结构osMemoryPoolId_t :内存池句柄未使用

osmemorypoolnewosmemorypoolid _ tosmemorypoolnew (uint 32 _ t block _ count,uint32_t block_size, const osMemoryPoolAttr_t* attr输入* block_count :内存池中的最大内存块数量* block_size :每个内存块的字节数* attr 内存池ID//注意ISR中此函数osmemorypoolgetnameconstchar * osmemorypoolgetname (osmemorypolid _ tmp _ 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 ,否则0if( Key_Scan(GPIOA,GPIO_Pin_0) == KEY_ON)mb_led->red = 0; // 红灯亮elsemb_led->red = 1; // 红灯灭 if(Key_Scan(GPIOC,GPIO_Pin_13) == KEY_ON)mb_led->green = 0; // 绿灯亮elsemb_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键按下:

小结

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

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

参考资料

☞官方资料

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。