前言
声明:本文从Enode之父汤雪华QQ群里提取的,分享给各位小伙伴们供参考。
双缓冲,顾名思义为两个List,此设计用于解决收发分离过程中最大限度避免锁的竞争、有效的降低CPU开销、降低锁的竞争冲突而生。
应用场景
双缓冲List的设计思路,应用在多线程产生消息,单线程处理消息的场景。
设计思路
声明2个list,分别为_requestsWrite,_requestsRead,写入的消息放入_requestsWrite,读取从_requestsRead读取;
再声明一个bool类型的_hasNotified,false表示_requestsWrite里没消息,true表示_requestsWrite有消息。通过_hasNotified判断是否需要交换引用地址。
再声明一个int类型的_requestsWriteThreshold,这个值可以根据自己的场景进行配置,如果没有这种阈值的设计,那如果_requestsRead处理慢,而_requestsWrite增长很快,那_requestsWrite很快就满了,把内存撑爆了。
消息接收后都放入_requestsWrite,通过lock解决并发问题,里面不是写文件,而是把消息先放入_requestsWrite中,这个动作非常快,所以lock的竞争开销很小;消息持久化采用单线程while(true){……}的方式,然后通过SpinWait.SpinOnce来避免CPU空转。核心思路就是把接收的数据先放入_requestsWrite,然后要处理的数据放入_requestsRead,处理总是面向_requestsRead并单线程处理;每次处理是判断_requestsWrite里是否有数据,如果有就和_requestsRead交换引用,然后处理_requestsRead;
如果处理_requestsRead的线程处理太慢,来不及处理,则我们只需要给_requestsWrite的容量设置一个阈值即可,即当_requestsWrite到达阈值时,就自动流控。
StoreMessageAsync是放入写_requestsWrite。
PersistMessages()这个方法是单线程运行,此方法主要职责是从_requestsRead里取要写入文件的所有消息,在一个while(true)里被调用,写的时候,总是一次性把_requestsRead里的所有消息都写入磁盘。写完后,再看看_hasNotified是否为true,如果是,则和_requestsWrite进行交换,然后继续写。一开始放入写list后,总是检查_hasNotified是否为false的,如果是false,就立即改为true
SwapRequests就是双缓冲队列的核心设计,负责交换地址。