队列与任务通信问答合集
由 15 篇星球问答重组而成,覆盖队列原理、缓冲区选型、零拷贝与生命周期、信号量与互斥量四大主题。原帖全部保留,见文末来源清单。
一、队列的原理与开销
队列的 item_size 和 queue_size 有什么区别?
item_size 是队列中每个元素的长度;queue_size 是队列能装多少个元素(队列长度)。整个队列的容量就是 queue_size × item_size。
— 队列中 item_size和queue_size的区别(Jack)
队列收发的数据到底存在哪、谁在拷贝?
队列创建后,其存储空间就在 FreeRTOS 管理的堆中;发送与接收的两次拷贝都发生在 FreeRTOS 堆内的这块内存上,由 CPU 完成。
— 老师,请问这个消息队列发送和接受消息时。是不是从任务栈内存中的一个变量拷贝到消息队列(Jack)
队列收发存在两次拷贝,怎么优化?
xQueueSend 拷一次、xQueueReceive 又拷一次——16 字节的结构体收发一轮要拷 32 字节。改法:队列里传指针,收发一轮只拷 8 字节(指针本身);改用 Task Notify 则只需一次拷贝。
— 我们在使用队列会存在两次数据拷贝,xQueueSend(...)发一次,(Jack)
消息缓冲区和消息队列有什么区别?
"消息缓冲区"是泛指自己搭建的缓冲结构(比如环形缓冲区);消息队列本质上也是消息缓冲区,但比自研的更严谨——内置了很多临界操作的预防动作。
— 请问消息缓冲区是什么东西 ?和消息队列有什么区别(Jack)
二、缓冲区选型与并发
环形缓冲区+队列怎么配合生产者/消费者?
1)生产者线程确定环形队列/队列中最后一个元素的地址;2)消费者线程通过消息队列或任务通知收到这个地址;3)消费者沿队列顺序依次处理,直到最后一个元素。
— 环形缓冲区和队列,在生产者和消费者线程中,如何应用(Jack)
一个生产一个消费,能不加锁吗?
可以:用无锁环形缓冲区,生产者只推进 head,消费者只推进 tail,双方互不改对方的索引。前提是单核——多核会乱序执行、引入竞态,数据接口必须用保护手段封装。
— 两个线程,一个生产,一个消费,能够一个缓冲区,不加锁解决吗:(Jack)
竞态背景补充:— 多核程序开发,考虑更多的是竞态,因为很可能会乱序执行各个线程(Jack)
共享内存怎么保护?
多任务访问同一块共享内存、且走相同接口时,一定要加临界区保护。
— 共享内存的操作,如果有相同接口,一定要一定要加临界区(Jack)
三、广播、流数据与零拷贝
消息队列怎么实现一发多收(广播)?
用共享 Memory / 共享流 buffer,而不是像"每个 task 一个队列、发送时遍历"那么粗暴。
— 如何实现消息队列的广播,一发多收,并且能保证每-个task都能收到完整消息:比如bu(Jack)
任务之间发不定长信息用什么机制?
典型的流 buffer(stream buffer)应用:一个在前面写、一个在后面追,如果"咬尾"了,说明线程优先级配置或系统负载已经超了。比"malloc 分配 + 队列传地址 + 收方负责 free"(收方得不到运行就内存泄漏)更稳。
— 任务之间通信,需要发不定长的信息,目前用什么机制实现比较好:(Jack)
邮箱(mailbox)是什么?
邮箱最主要的特点是内部元素长度只有四字节,一般用来存指针;而且不局限于在队列上实现——Task Notify 同样是四字节传递,其实更适合做邮箱。
— 关于邮箱? 邮箱最主要的特点就(Jack)
零拷贝传地址时,变量生命周期怎么考虑?
静态存储区变量是"永生"的,堆由自己释放,栈变量随函数退出消失。实现零拷贝时,把变量地址传给另一个线程后,该变量必须仍然存在(未被回收),接收方才能读到——生命周期由定义位置决定。
— 零拷贝时,对于变量生命周期的思(Jack)
四、信号量与互斥量
二值信号量和互斥信号量各用在什么场合?
二值信号量:中断↔线程、线程↔线程之间都可以;互斥锁:只能用于线程↔线程之间。
— 二值信号量主要是线程间通信吗?互斥信号量用在什么时候(Jack)
互斥量的优先级继承,释放锁后会把优先级降回去吗?
会。低优先级任务 A 持锁、高优先级任务 B 等锁时,A 被临时抬到 B 的优先级;B 成功拿到锁后,A 恢复为原来的低优先级。
— 老师: 使用互斥信号量,低优先(Jack)
高(H)/中(M)/低(L) 三任务抢一把锁,L 会压过 M 吗?
会。L 持有 Mutex 时优先级被临时提升到 H,因此不需要锁的 M 抢不过 L,L 继续运行直到释放——这正是优先级继承抑制"中间优先级插队"(优先级反转)的机制。
— 老师我有个问题: 有三个任务,(Jack)
来源笔记
- 队列中 item_size和queue_size的区别(Jack)
- 老师,请问这个消息队列发送和接受消息时。是不是从任务栈内存中的一个变量拷贝到消息队列(Jack)
- 我们在使用队列会存在两次数据拷贝,xQueueSend(...)发一次,(Jack)
- 请问消息缓冲区是什么东西 ?和消息队列有什么区别(Jack)
- 环形缓冲区和队列,在生产者和消费者线程中,如何应用(Jack)
- 两个线程,一个生产,一个消费,能够一个缓冲区,不加锁解决吗:(Jack)
- 多核程序开发,考虑更多的是竞态,因为很可能会乱序执行各个线程(Jack)
- 共享内存的操作,如果有相同接口,一定要一定要加临界区(Jack)
- 如何实现消息队列的广播,一发多收,并且能保证每-个task都能收到完整消息:比如bu(Jack)
- 任务之间通信,需要发不定长的信息,目前用什么机制实现比较好:(Jack)
- 关于邮箱? 邮箱最主要的特点就(Jack)
- 零拷贝时,对于变量生命周期的思(Jack)
- 二值信号量主要是线程间通信吗?互斥信号量用在什么时候(Jack)
- 老师: 使用互斥信号量,低优先(Jack)
- 老师我有个问题: 有三个任务,(Jack)