一、介绍
消息队列跟邮箱差不多,都是一种通信系统,就好比微信,可以让你跟好朋友自如的交换信息。
消息是一种遵循先进先出的原则,比如装酒的漏斗,先倒进去的先流出来,是一个道理的。哪个线程先放数据,那么其他线程在读的时候,一定是先读到最早放入的那个数据,看下面的示意图就明白了。
二、使用
使用消息队列传输数据时有两种方法:
- 拷贝:把数据、把变量的值复制进消息队列里
- 引用:把数据、把变量的地址复制进消息队列里
只要知道消息队列的句柄,谁都可以读、写该消息队列。线程、ISR 都可读、写消息队列。可以多个线程读写消息队列。线程读写消息队列时,如果读写不成功,可以即刻返回错误,也可以阻塞。阻塞时可以指定超时时间。口语化地说,就是可以定个闹钟:如果能读写了就马上进入就绪态,否则就阻塞直到超时。
比如某个线程读消息队列时:
如果消息队列中有可用的消息,即刻得到消息并返回
如果消息队列中没有可用的消息,线程有两种选择:即刻放回一个错误值,或者阻塞一段时间
如果线程阻塞,它何时被唤醒?
在指定的时间内,别的线程或者中断服务程序写了队列,会把它唤醒
否则,指定的时间到达后超时返回错误。既然读写消息队列的线程个数没有限制:
多个线程都想写队列,但是队列已经满了,这些线程可以进入阻塞状态:它们都在等待队列有空间
那么:队列有空间时,把那个线程唤醒?
多个线程都想读队列,但是队列已经空了,这些线程可以进入阻塞状态:它们都在等待队列有数据
那么:队列有数据时,把那个线程唤醒?
唤醒谁?有两种方法。创建队列时,可以指定一个参数 flag
RT_IPC_FLAG_PRIO:表示唤醒优先级最高的等待线程
RT_IPC_FLAG_FIFO:表示唤醒等待时间最长的等待线程
代码: 这里也是创建了两个线程,一个发送一个用来接收。我们这里传的是i变量的地址,接收也是获取i的地址,
#include "board.h" //头文件合集
#include <stdio.h>
struct rt_thread thread1_send_mb; //发送邮箱
struct rt_thread thread2_recv_mb;//接收邮箱
/*队列句柄 */
static rt_mq_t mq = RT_NULL;
rt_uint8_t thread1_stack[512]={0};
rt_uint8_t thread2_stack[512]={0};
char send_arr[]="i love thread";
static void thread1_callback(void *parameter)
{
rt_uint32_t i;
while(1)
{ //发送
rt_mq_send(mq,&i,4);
i++;
rt_kprintf("mq succeed!\r\n");
rt_thread_delay(500);//延时500ms,让thread2可以去运行
}
}
static void thread2_callback(void *parameter)
{
rt_uint32_t i=-1;
while(1)
{ //接收队列数据
rt_mq_recv(mq,&i,4,RT_WAITING_FOREVER);
rt_kprintf("mq data:%d\r\n",i);
rt_thread_delay(500);//延时500ms,让thread1可以去运行
}
}
int main(void)
{ //FIFO模式,先进先出
mq = rt_mq_create("mq",4,1, RT_IPC_FLAG_FIFO);
rt_thread_init(&thread1_send_mb,
"thread1",
thread1_callback,
NULL,
&thread1_stack[0],
sizeof(thread1_stack),
7,
5);
/*5是时间片,作用是当有相同优先级的线程时,最大执行时间是5 tick
如果线程毁掉函数执行时间少于5 tick,也会提前返回,切换带到下一个线程
如果5 tick还没执行完也会切换到下一个线程 */
rt_thread_init(&thread2_recv_mb,
"thread2",
thread2_callback,
RT_NULL,
&thread2_stack[0],
sizeof(thread2_stack),
8,
5);
//开启2个线程
rt_thread_startup(&thread1_send_mb);
rt_thread_startup(&thread2_recv_mb);
return 0;
}
结果演示: