单片机实现环形队列_银行告诉你什么是无锁队列

本文详细介绍了单片机实现的无锁队列原理,通过银行排队场景类比,阐述了无锁队列的概念。文章通过逐步优化,从环形队列到无锁队列,分析了不同版本的实现方式和效率问题,特别是无锁队列的高效数据拷贝策略。同时,文章指出了无锁队列中的一些潜在问题和解决办法,包括使用内存屏障和数据缓存的影响,强调了在多任务环境下无锁队列的正确性和效率。最后,提出了测试无锁队列的方法,包括数据规律检查和中断压力测试,以确保代码的稳定性和可靠性。
摘要由CSDN通过智能技术生成

b20364410711130df435c056ae1f1d36.gif

来源:公众号:【鱼鹰谈单片机】

作者:鱼鹰Osprey

ID:emOsprey

c7cbf553655ecb411e1fdbe21a5de903.gif

这篇长文除了由浅入深的一步步迭代出无锁队列的实现原理,也会借此说说如何在项目中注意避免写出有BUG的程序,与此同时也会简单聊聊如何测试一段代码,而这些能力应该是所有软件开发工作者都应该引起注意的。而在介绍的过程中也会让你明白理论和实际的差距到底在哪。

高级程序员和初级程序员之间,鱼鹰认为差距之一就在于写出来的代码BUG多还是少,当然解决BUG的能力也很重要。

既要有挖坑的能力,也要有填坑的实力!

其实在避免BUG这一块,鱼鹰不管是在前面的《延时功能进化论》,还是《引脚输出的隐藏BUG》,甚至于鱼鹰最先思考的关于信号量保护那里都有介绍过,如果你能真正理解文章所介绍的那些情况,那么写一个可靠的程序应该不难。

而关于队列,鱼鹰也在很早之前就写过一篇《数据结构系列之队列FIFO》,只是那个时候总觉得那种方式效率太低(后面会说为什么效率低),应用场合太少。

很早之前鱼鹰就听说过无锁队列,但由于以前的项目不是很复杂,很多时候都可以不需要无锁队列,所以就没有更深入的学习,直到这次串口通信想试试无锁队列的效果,才最终用上了这一神奇的代码。

网上有个词形容无锁队列鱼鹰觉得很贴切:巧夺天工

现在就从队列开始说起吧。

什么是队列,顾名思义,就类似于超市面前排起的一个队伍,当最前面的顾客买完了东西,后面的顾客整体向前移动,而处于新队头的顾客继续消费。如果没有后来的顾客,那么最终这个队伍将消失。而如果有新的顾客到来,那么他将排在队伍最后等待购买。

554e1c3c0fa5bc060697e2cbcc5b11d3.gif

(忽略后面那个捣蛋的你)

对于顾客队伍来说,消费者,在这里其实是超市(不是顾客),因为它让这个队伍越来越短,直至消失;而生产者,在这个模型中无法看到,只能说是市场需求导致队伍越来越长。

如果对此较难理解的话,不如说说目前口罩购买情况。

如果市场上所有的口罩会自动排队的话,那么购买口罩的人就是口罩队伍的消费者,而生产口罩的厂家就是生产者。因为厂家生产的少,而购买者多,所以才会出现供不应求的情况,而一旦厂家产能上来了,供需就能平衡,而这种平衡不仅是市场需要的,也是软件所追求的目标。

说回超市模型,如果我们用软件模拟这种排队情况,应该怎么做呢?

声明一个足够大的数组,后面来的数据(顾客)总是放在最后面,一旦有程序取用了数据,那么整体向前移动,后面来的数据继续放在最后。(这里需要一个变量指示队伍的长度,毕竟不是真实的排队场景,无法直接从队伍本身看出哪里是队尾)。

这样确实很好的模拟了现实中的情况,但对于软件来说,效率太低!

对于顾客而言,所有的顾客每次前进一小步,感觉好像没什么,也很正常,毕竟最终等待的时间还是一样的,但是每隔一小段时间就要动一动,还是挺麻烦的事情。

而对于程序而言,不仅仅是移动数据需要耗费CPU,更重要的是在移动过程中,如何保证“消费者”能正确提取数据,“生产者”能正确插入数据。毕竟你在移动数据的过程中有可能消费者需要消费数据,而生产者需要生成数据,如何进行同步呢?

那么是否有更好的方式呢?

现实其实已经给了我们答案。

不知道你是否发现一个现象,银行里面一排排的椅子上坐满了人,但是银行窗口前却没有了长长的队伍?这是怎么回事?为什么那些人不再排队了,他们不怕有人插队吗?

当你亲自体验了之后就知道,没人会插你的队,为什么?

当你走进银行时,服务员会提醒你去一台机器前领取一个纸质号码,然后就请你在座位上等着。

等了一会,银行内广播开始播报“9527,请到2号柜台办理业务”,你一听,这不就是自己刚领的号嘛,所以你马上就到窗口前办理需要的业务了。

那么这是如何实现的?为什么你不再需要站着排队了,也不需要一点一点的向前挪动,而是一直坐着就把队给排了

这个关键就在那台机器。

每来一个人领取号码,机器就把当前的计数值给顾客,然后增加当前的计数,作为下一位顾客使用的号码;而每当银行柜员服务完一位顾客,机器就会自动播报本次已服务对象号码的下一个号码前来办理业务,并自加另一个计数值,作为下一次办理业务的对象。而当两计数器值相等时,就代表该柜员完成了所有顾客的服务。

所以,不是没有排队,而是机器在帮你排队。

那么这种机制在程序中该如何实现呢?

在这里我们先假设只有一个柜台能办理业务(实际银行是有多个柜台,这里暂时不考虑那么复杂),一个机器为顾客发放编号。

首先需要两个变量充当机器的职能:

in:初始化为 0,当来了顾客,将当前值发给客户,然后自动加 1

out:初始化为 0,当柜员需要请求下一位顾客前来服务时,服务顾客编号为当前值,然后自动加 1

因为纸条上可展示的位数是有限的,所以我们可以先假设为4位数,即两个变量值范围是0~9999(还好STM32F103 RAM内存足够)。

然后需要一个元素容量为10000大数组,存放目前排队顾客的编号,因为数组中存放的是0~9999的编号,所以数组的声明采用uint16_t 进行声明,这样才能把编号存放在数组单元中。

现在看代码(RT-Thread系统):

70b300a9aad28c5f6e61ea26e9c465f9.png

c99d0316239f2bf71ec8dfbdcef350a2.png

现在假设柜员刚开始上班,他不清楚有没有顾客等待服务,所以他首先使用 get函数获取自己的服务顾客编号,但是因为柜员来的有点早,顾客还没来银行办理业务,所以第一次机器告诉柜员没有顾客等待服务。

c19a46b2fd9ccff772e027fd2acf4123.png

后来陆续来了三位顾客,柜员再一次获取时,编号为 0 的顾客开始前来办理业务,柜员服务完之后,接着继续把剩下两位的业务都办理完成,第四次获取时,柜员发现没有顾客等待了,所以柜员可以休息一下(实际情况是,柜员前应该有显示当前排队人数,当排队人数为0时,就不会去使用get函数了)

0f2057ff2dacd7d8fc08ebba00ca5766.png </
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值