Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
DIY综合交流区
[RealTouch例程]消息队列的基本使用
发布于 2012-08-18 15:05:10 浏览:13561
订阅该版
实验目的 ? 了解消息队列的基本使用 ? 熟练使用消息队列实现多个线程间通信 硬件说明 本实验使用RT-Thread官方的Realtouch开发板作为实验平台。涉及到的硬件主要为: ? 串口3,作为rt_kprintf输出 需要连接JTAG扩展板,具体请参见《Realtouch开发板使用手册》 实验原理及程序结构 实验设计 本实验演示消息队列在RT-Thread中作为线程间通信的手段是如何使用的。 实验中创建三个线程,线程1从消息队列中获取消息;线程2向消息队列中发送普通消息,即消息会被放在消息队列的尾部;线程3向消息队列中发送一个紧急消息,即这个消息会被放在消息队列的头部。 本实验同样使用静态消息队列来作为演示,涉及静态消息队列初始化/脱离。动态消息创建/删除类似,不再赘述。 源程序说明 本实验对应kernel_message_queue 系统依赖 在rtconfig.h中需要开启 ``` #define RT_USING_HEAP``` 此项可选,开启此项可以创建动态线程和动态互斥量,如果使用静态线程和静态互斥量,则此项不是必要的 ``` #define RT_USING_MESSAGEQUEUE``` 此项必须,开启此项后才可以使用消息队列相关API ``` #define RT_USING_CONSOLE``` 此项必须,本实验使用rt_kprintf向串口打印按键信息,因此需要开启此项 主程序说明 在applications/application.c中定义静态消息队列控制块、存放消息的缓冲区。如下所示 定义全局变量代码 ```#define MSG_VIP "over" /* 消息队列控制块 */ static struct rt_messagequeue mq; /* 消息队列中用到的放置消息的内存池 */ static char msg_pool[2048]; ```在applications/application.c中的 int rt_application_init()函数中,初始化消息队列代码如下所示。 初始化消息队列代码 ``` rt_err_t result; /* 初始化消息队列 */ rt_mq_init(&mq, "mqt", &msg_pool[0], /* 内存池指向msg_pool */ 128 - sizeof(void*), /* 每个消息的大小是 128 - void* */ sizeof(msg_pool), /* 内存池的大小是msg_pool的大小 */ RT_IPC_FLAG_FIFO);/* 如果有多个线程等待,按照先来先得到的方法分配消息 */ if (result != RT_EOK) { rt_kprintf("init message queue failed. "); return -1; } ```在int rt_application_init()初始化名为”thread1”的thread1的静态线程,如下所示。 初始化线程1代码 ``` rt_thread_init(&thread1, "thread1", thread1_entry, RT_NULL, &thread1_stack[0], sizeof(thread1_stack),10,50); rt_thread_startup(&thread1); ```其线程入口函数如下所示,线程1不停地从消息队列中取出消息,打印消息内容,并检测是否为特殊消息(宏MSG_VIP表示),若是,则线程1不再循环接收邮件,从while循环中调出,线程函数运行结束;若否,则继续从消息队列中获取消息。 如果消息队列中没有消息,则线程1会被挂起,直到消息中有消息时,线程1会被唤醒,从挂起变为就绪态。 线程1代码 ```ALIGN(RT_ALIGN_SIZE) //设置下一句线程栈数组为对齐地址 static char thread1_stack[1024]; //设置线程堆栈为1024Bytes struct rt_thread thread1; //定义静态线程数据结构 /* 线程1入口 */ static void thread1_entry(void* parameter) { char buf[128]; while (1) { rt_memset(&buf[0], 0, sizeof(buf)); /* 从消息队列中接收消息 */ if (rt_mq_recv(&mq, &buf[0], sizeof(buf), RT_WAITING_FOREVER) == RT_EOK) { rt_kprintf("thread1: recv msg from msg queue, the content:%s ", buf); /* 检查是否收到了紧急消息 */ if (strcmp(buf, MSG_VIP) == 0) break; } /* 延时1s */ rt_thread_delay(RT_TICK_PER_SECOND); } rt_kprintf("thread1: got an urgent message, leave "); } ```在int rt_application_init()初始化名为”thread2”的thread2的静态线程,如下所示。 初始化线程2代码 ``` rt_thread_init(&thread2, "thread2", thread2_entry, RT_NULL, &thread2_stack[0], sizeof(thread2_stack),10,50); rt_thread_startup(&thread2); ```其线程入口函数如下所示,线程2不停地向消息队列中发送消息,直到消息缓冲区被充满后,跳出循环,调用rt_kprintf打印结束信息后,线程函数运行结束。 线程2代码 ```ALIGN(RT_ALIGN_SIZE) //设置下一句线程栈数组为对齐地址 static char thread2_stack[1024]; //设置线程堆栈为1024Bytes struct rt_thread thread2; //定义静态线程数据结构 /* 线程2入口 */ static void thread2_entry(void* parameter) { int i, result; char buf[128]; i = 0; while (1) { rt_snprintf(buf, sizeof(buf), "this is message No.%d", i); /* 发送消息到消息队列中 */ result = rt_mq_send(&mq, &buf[0], sizeof(buf)); if ( result == -RT_EFULL) break; rt_kprintf("thread2: send message - %s ", buf); i++; } rt_kprintf("message queue full, thread2 leave "); } ```在int rt_application_init()初始化名为”thread3”的thread3的静态线程,如下所示。 初始化线程3代码 ``` rt_thread_init(&thread3, "thread3", thread3_entry, RT_NULL, &thread3_stack[0], sizeof(thread3_stack),10,50); rt_thread_startup(&thread3); ```其线程入口函数如下所示,线程3入口函数中,首先使用rt_thread_delay延时5秒钟,使得线程1和线程2充分运行,之后调用rt_mq_urgent函数向消息队列中发送紧急消息,这个函数会将消息插入到消息队列头部,因此接收消息的线程会优先取得。 如果此时消息队列已经满,则延时20个tick后重复发送,直到正确发送紧急消息,发送成功后线程3入口函数执行完毕退出。 ```ALIGN(RT_ALIGN_SIZE) //设置下一句线程栈数组为对齐地址 static char thread3_stack[1024]; //设置线程堆栈为1024Bytes struct rt_thread thread3; //定义静态线程数据结构 /* 线程3入口函数 */ static void thread3_entry(void* parameter) { char msg[] = MSG_VIP; int result; rt_thread_delay(RT_TICK_PER_SECOND * 5); rt_kprintf("thread3: send an urgent message <%s> ", msg); /* 发送紧急消息到消息队列中 */ do { result = rt_mq_urgent(&mq, &msg[0], sizeof(msg)); if (result != RT_EOK) rt_thread_delay(20); } while (result != RT_EOK); } ```3 编译调试及观察输出信息 编译请参见《RT-Thread配置开发环境指南》完成编译烧录,参考《Realtouch开发板使用手册》完成硬件连接,连接扩展板上的串口和jlink。 运行后可以看到如下信息: 串口输出信息 | / - RT - Thread Operating System / | 1.1.0 build Aug 9 2012 2006 - 2012 Copyright by rt-thread team thread2: send message - this is message No.0 thread2: send message - this is message No.1 thread2: send message - this is message No.2 thread2: send message - this is message No.3 thread2: send message - this is message No.4 thread2: send message - this is message No.5 thread2: send message - this is message No.6 thread2: send message - this is message No.7 thread2: send message - this is message No.8 thread2: send message - this is message No.9 thread2: send message - this is message No.10 thread2: send message - this is message No.11 thread2: send message - thread1: recv msg from msg queue, the content:this is message No.0 tg queue, the content:thread2: send message - this is message No.13 thread2: send message - this is message No.14 thread2: send message - this is message No.15 message queue full, thread2 leave thread1: recv msg from msg queue, the content:this is message No.1 thread1: recv msg from msg queue, the content:this is message No.2 thread1: recv msg from msg queue, the content:this is message No.3 thread1: recv msg from msg queue, the content:this is message No.4 thread3: send an urgent message
thread1: recv msg from msg queue, the content:over thread1: got an urgent message, leave 结果分析 整个程序运行过程中各个线程的状态变化: rt_application_init中创建了三个线程,thread1(线程1)、thread2(线程2)、thread3(线程3),它们具有相同的优先级和时间片配置。由于先使用rt_thread_startup(&thread1),故线程1优先运行,其次运行thread2,最后运行thread3。 在线程1中,首先从消息队列中接收消息,此时消息队列中并没有消息,线程1被挂起,内核会调度线程2运行。在线程2的线程处理函数中,不停使用rt_mq_send向消息队列中发送消息。注意,在rt_mq_send这个函数中,线程1就已经从挂起状态恢复为就绪状态,只是线程2和线程1优先级相同,因此线程2继续运行,可以从终端看到如下信息: thread2: send message - this is message No.0 ……… thread2: send message - this is message No.10 thread2: send message - this is message No.11 接下来的信息需要仔细分析: thread2: send message - thread1: recv msg from msg queue, the content:this is message No.0 tg queue, the content:thread2: send message - this is message No.13 红色字体是线程2要发送的字符串,显然它没有发送完整;绿色字体为线程1发送字符串,因此我们可以得出结论,线程2的时间片在发送完红色字符串后耗尽。由于三个线程优先级相同,线程3依然处于挂起状态,因此线程1被调度执行,在线程1处理函数中,从 rt_mq_recv函数返回,接下来调用 rt_kprintf("thread1: recv msg from msg queue, the content:%s ", buf); 将接收到的消息输出到串口上。 接下来线程1执行rt_thread_delay延时1秒中,线程1被挂起,此时线程3依然处于挂起状态,线程2继续运行,故输出 thread2: send message - this is message No.13 之后线程2继续向消息队列中发送消息,并同时向串口输出 thread2: send message - this is message No.14 thread2: send message - this is message No.15 此时,消息队列已满,thread2打印如下信息后,线程2处理函数退出。 message queue full, thread2 leave 一段时间之后(刚才线程1不是被挂起1秒钟么),线程1调度运行,并打印 thread1: recv msg from msg queue, the content:this is message No.1 继续休眠1秒钟,内核执行IDLE线程,1秒钟后,线程1恢复运行,再次获取消息,重复这个过程。在这个循环的过程中,向串口上打印如下信息: thread1: recv msg from msg queue, the content:this is message No.1 thread1: recv msg from msg queue, the content:this is message No.2 thread1: recv msg from msg queue, the content:this is message No.3 thread1: recv msg from msg queue, the content:this is message No.4 接下来串口信息为: thread3: send an urgent message
这表明线程3的5秒定时终于到达,线程3被唤醒,并在线程1某个休眠的时间中调度运行,它会打印上面这条信息,并使用rt_mq_urgent函数向消息队列中发送一条紧急消息,之所以称之为紧急消息,是因为这个函数会将消息插入到消息队列的头部。之后线程3的处理函数也执行完毕退出。 一段时间后,线程1调度运行,它从消息队列中获取消息,会得到刚才线程3发送的紧急消息,线程1跳出接收循环,打印 thread1: recv msg from msg queue, the content:over thread1: got an urgent message, leave 线程1的线程处理函数也退出。 以上就是整个实验中,各个线程的状态转换过程。 5 总结 本实验演示了RT-Thread中消息队列作为多线程通信的用法,以静态消息控制块为例,动态消息队列的用法类似,只是创建/删除需要使用 rt_mq_create/rt_mq_delete函数,读者可以使用动态消息队列重复本实验。 [attach]0[/attach] 下载附件 [实验2_8消息队列基本使用.pdf](https://oss-club.rt-thread.org/uploads/88_f38be0a0069cdbee1e84467df990d751.pdf) 下载附件 [1_kernel_message_queue.zip](https://oss-club.rt-thread.org/uploads/3089_b66479d9ece8241ffc9a3b6f9f075557.zip)
查看更多
15
个回答
默认排序
按发布时间排序
jichong211
2012-08-18
这家伙很懒,什么也没写!
能不能讲讲什么时候用mailbox 什么时候用messagequeue。 ucos的消息队列实际上就是rt-thread的mailbox。mailbox配合malloc和free也可以实现messagequeue的功能,区别在于这个消息所占用的内存是临时申请的还是提前静态分配好的。临时申请可能失败,静态分配可能溢出和浪费。messagequeue应该可以在中断中使用吧,而mailbox+malloc则不行。
shaolin
2012-08-18
这家伙很懒,什么也没写!
好的,需要的就是这样的需求和反馈。后面专门讲讲这部分吧;
dennis
2012-08-23
这家伙很懒,什么也没写!
我把程序搬到开发板,发现 线程2 无法结束,消息队列 一直满不了
bloom5
2012-09-05
这家伙很懒,什么也没写!
添加例程 [attach]1285[/attach]
咖啡恋
2012-09-07
这家伙很懒,什么也没写!
"tg queue, the content:thread2: send message - this is message No.13" 输出结果的该条语句中:“tg queue, the content:”这部分是从哪打印出来的?
tracy
2012-11-08
这家伙很懒,什么也没写!
>我把程序搬到开发板,发现 线程2 无法结束,消息队列 一直满不了 --- 我的也是,谁能解释一下。谢谢。
tracy
2012-11-08
这家伙很懒,什么也没写!
>我把程序搬到开发板,发现 线程2 无法结束,消息队列 一直满不了 --- 我找到原因了,初始化队列的时候设定的消息长度是128 - sizeof(void*) = 124,但是在线程1和线程2实现的时候,设定的消息长度是128,这个在发送消息和接收消息函数里面都有判断这个长度,所以会有错误。
sxf_zero
2012-12-24
这家伙很懒,什么也没写!
能不能一次从消息队列中读取多条消息,每次读取一条效率不高啊
sxf_zero
2012-12-24
这家伙很懒,什么也没写!
能不能实现这样的功能,指定要读取的消息数量,返回值为实际读取到的数量
aozima
2012-12-24
调网络不抓包,调I2C等时序不上逻辑分析仪,就像电工不用万用表!多用整理的好的文字,比截图更省流量,还能在整理过程中思考。
>能不能实现这样的功能,指定要读取的消息数量,返回值为实际读取到的数量 --- 假如你去写这样一个,不也还是一次收取一个,直到为空? 和原来又有何区别?
撰写答案
登录
注册新账号
关注者
0
被浏览
13.6k
关于作者
shaolin
这家伙很懒,什么也没写!
提问
115
回答
444
被采纳
0
关注TA
发私信
相关问题
1
[项目]搞个开源的硬件项目
2
硬件计划贴,及时更新,欢迎提意见
3
软件计划贴,及时更新,欢迎提意见::WMA,MOUNT,LWIP等问题急需解决.
4
MMS协议
5
定点的wma解压库-libwma
6
QQ群记录 [20090821]
7
STM32网络收音机PCB报名征集
8
第一版调试记录
9
第二版硬件讨论
10
RADIO项目相关模块规格--欢迎大家自己做板时规格与此兼容,减少重复劳动
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
最新文章
1
开源共生 商业共赢 | RT-Thread 2024开发者大会议程正式发布!
2
【24嵌入式设计大赛】基于RT-Thread星火一号的智慧家居系统
3
RT-Thread EtherKit开源以太网硬件正式发布
4
如何在master上的BSP中添加配置yml文件
5
使用百度AI助手辅助编写一个rt-thread下的ONVIF设备发现功能的功能代码
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
keil_MDK
rt_mq_消息队列_msg_queue
MicroPython
ulog
C++_cpp
本月问答贡献
踩姑娘的小蘑菇
7
个答案
3
次被采纳
a1012112796
19
个答案
2
次被采纳
张世争
9
个答案
2
次被采纳
rv666
6
个答案
2
次被采纳
用户名由3_15位
13
个答案
1
次被采纳
本月文章贡献
程序员阿伟
9
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
RTT_逍遥
1
篇文章
5
次点赞
大龄码农
1
篇文章
5
次点赞
ThinkCode
1
篇文章
1
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部