首先看下go的chan内部源码 chan 内部是一个固定长度的数组,这个数组又是环形队列。qcount数组实际大小,dataqsize数组长度,buf为实际数组指针,sendx,recvx为接受发送索引,用于环形队列,自增超过dataqsize索引清0,recvq,sendq链表保存等待或者发送groutinue信息,他是先进先出的队列。lock,go的chan的锁实际是自旋锁。
让我们在看下lock的代码
go的自旋锁实际采用tas testandset,当tas次数<4次的时候,procyyield内部汇编是空转cpu,<5 yield交出时间片给其他groutinue,其他情况,就一直tas一直到成功为止
go chan 再写入和读取都是自旋锁,锁队列。
环形队列,如下
在探讨下godisruptor为什么不适合go的并发开发
1.首先语法,godisruptor无锁环形队列,写法不能再select了,只能groutine + for模式
2.godisruptor内部没有优化好,再消费者,实际他只是sleep等待,具体源码如下
3.godisruptor生产者 一次入队10个的情况下性能比chan要好10个倍数。但是再 多生产者但消费者,生产者实际产生的io,一般都是一条一条入的实际情况下,性能和chan差不都,自旋和无锁区别,自旋消耗再锁操作,会出现空转cpu,无锁利用cpucacheline+环形队列+cas,性能更加,利用upper和lower一个作为生产者索引,一个做消费者索引,当消费者索引<生产者索引的时候,取数据,内部这两个变量是一直累加&数组长度,无锁也只要cas这两个索引就ok,就避免了统一时刻操作数组同一个索引。
数据如下 2千万数据, 5个生产者一个消费者 单入的情况下,godisruptor每秒5790387吞吐,chan每秒吞吐3696857,一个倍级
对比数据,你是选godisruptor还是chan
原文 https://studygolang.com/articles/22537