关于队列大致的基础知识我自己简单了解了一下,针对我自己脑子里还有的我认为我可能很快用到的知识点做一个如下的总结。
- V9.0.0之后,队列可以做成静态的,编译前就确定下来。
- 一般来说,队列是从heap中进行存储分配的,存储分配释放的过程则是OS来处理;
- 队列可以容纳一定数目的特定对象,最大的数目被成为队列长度;
- 队列的主要目的是提供任务与任务、任务与中断之间的信号通信方式。确切说,存在任务到任务,任务到中断,中断到任务等几种方式。
- FreeRTOS采用的是拷贝方式实现队列数据传输而不是引用,教程中提到了里面的很多好处,但是我觉得它忘了说明一下引用方式在实时性上应该更有优势。
- 一般来说,队列会存在多发单收的情况,多收的情况,但是也允许多收的实现。
- 使用队列的Task在收发上可能会存在阻塞状态,也有阻塞时间参数可以设计,其中的一个关键区分的条件是非阻塞的任务被认为拥有较高优先级,同优先级阻塞任务中,等待时间长的被看成拥有更优先的队列资源使用权,不管是收还是发。
- 部分接口在中断中不能用,可能会引发问题。
- 队列的实现模型是FIFO,发送本质上存在发送到队前或者队后两种方式,发送到队后是一种默认的方式,而发送到队前有覆盖有效数据的可能。
- API中存在队列有效数据数目的查询函数,其他的API也有不同的提示机制。
好,大致先总结这么多。看看官方的演示例程:
上面代码,创建了一个长度为5的队列,可以容纳5个int32_t的数据。创建了3个Task,其中两个Task中执行了队列发送,一个执行了队列的接收。接收队列的Task的优先级要高一些。
几个Task的代码:
任务应该是非阻塞型的任务,看一下执行结果:
执行之后,不断打印出接收到的结果。这样的观测还是偏难,因为打印速度太快,我看看尝试改成阻塞任务。
可以看得出,很多时候是接收不到的,但是任务似乎有一定的查询频次。增加发送的频次,延时时间改成100ms,效果如下:
延时时间改成10ms。
这样,没有接收失败了。从理论上分析,应该是5ms发送一次。如果,接收的Task查询速度超过5ms。能够达到5ms两次应该就会出现接收失败。现在为什么没有接收失败呢?应该是设置了等待时间的原因。我把等待时间从100ms改成10ms,出现如下效果:
这个并不是一定会有,有时候会有连续接收的情况。理论分析上,按理说是应该全都能够读到。可能是优先级问题导致了多次读取?如果是这样,
再次测试,等待参数调整为20之后,没有获取不到的问题。两次发送出现的最大时间间隔应该是接近10ms,偏差应该也就是一个tick上下。如果这么考虑,是不是等待时间达到12之后就不会有收不到的情况?
测试了一下,果真还就跟我想的一致。这个问题大致分清楚了,还有一个问题需要解决,那就是队列发送的速度如果超过tick的速度会出现什么情况?应该说,这里应该加一个强制的任务切换,否则的话应该有大量的task会出现delay。其他的问题,暂时倒是没有考虑到更多。
最后增加一个测试,还是关于调度的。我想知道调度一般是在何时发生?回到最初的软件,我直接增加一个队列size的打印。部分代码修改:
从最初的注释看,这个任务可以立即阻塞数据的写入。这个应该可以这样理解,这个任务执行的时候,写入的Task应该不再执行了,毕竟这个Task的优先级高。但是,这有点说不过去,这不就意味着永远收不到消息吗?我理解这个应该是0或者1才对。通过测试,发现这里确实是如同注释所说,数值一直是0。
再次理解一下教程中的描述吧,应该我对这种机制中任务调度的理解有一点偏差了。
简单翻译一下:
向队列发送数据的Task的优先级要低于从队列接收数据的任务的优先级。这意味着队列应该永远不可能包含多余一个的对象,因为只要数据被发送到队列接收的task就会处于非阻塞状态从而中断发送任务,继而移除数据让队列重新变成空。
看上去似乎多少理解一点了,但是首先需要明确的一点是队列发送动作应该会触发一次任务调度,否则上面的这段描述就没意义了。其次,接收任务中之所以一直数目为0,应该是第一次执行后,接收任务就已经开启了队列等待模式,因为它有一个阻塞时间参数的存在。基于这些分析,应该可以做一个测试验证,那就是把接收任务的阻塞时间去掉。
实际测试下来,队列的大小从打印效果看依然是0。看起来,这个工作模型的理解不应该如此理解。应该是只要队列接收的功能执行过,接收任务就进入阻塞状态等待被发送任务激活。如果这样,修改任务优先级应该可以出现队列数目不为0的情况。
测试效果:
应该猜测的差不多了,值得一说的是我附加的另一个测试,我把发送的阻塞时间去掉,一瞬间软件就卡死了。看起来队列应该是很快就增加到了5个成员了。