不懂就问, Golang 带阻塞的高性能队列最佳实践是啥?

2020-04-11 11:38:47 +08:00
 lithbitren
像 Map 类型,可以无脑加锁实现共享数据,也可以直接用 sync.Map 来实现,前者在数据规模增大时性能会陡降,后者性能则平滑稳定,和一般语言一样,相同的需求,优先选择标准库里有的一般都是最佳的。

可是对于队列来说,不管是 fifo 队列,还是栈和堆,在标准库里好像并没有并发的实现,无脑加锁倒是可以,参考 sync.Map 源码写一个理论上也不是不行,但有没有更优或更简洁的实现方法呢,最好是满足定容队列满了 put 方法和队列为空时 get 方法都自带阻塞的。
5147 次点击
所在节点    Go 编程语言
21 条回复
unixeno
2020-04-11 11:43:42 +08:00
chan
useben
2020-04-11 11:47:59 +08:00
chan 就是你说的特性...
lithbitren
2020-04-11 12:01:56 +08:00
@useben
@unixeno
chan 可实现不定容的读写阻塞,但咋实现定容的写入阻塞啊?
DCCooper
2020-04-11 12:03:12 +08:00
@lithbitren #3 buffer ?
felix021
2020-04-11 12:05:42 +08:00
go 的 channel 貌似底层实现用的还是锁,github 上有第三方的 lockfree ring buffer,具体叫啥忘了,可以搜一下。
lithbitren
2020-04-11 12:06:11 +08:00
@DCCooper 是说类似于 make(chan int 10)这种写法吗?
susecjh
2020-04-11 12:08:29 +08:00
@lithbitren make(chan int, 10)这样不就是定容吗
feelinglucky
2020-04-11 12:08:30 +08:00
@lithbitren 定容的意思是固定容量吗?或者并发数?那直接设个长度不就好了吗…
lithbitren
2020-04-11 12:10:04 +08:00
chan 还有问题,不知道无法监控容量情况,而且也只是实现了 fifo,实现 filo 和优先队列似乎也挺麻烦。
reus
2020-04-11 12:10:08 +08:00
思而不学
zhs227
2020-04-11 12:27:36 +08:00
你说的这个可以参考一下 buffered chan 。申请 chan 的时候指定长度即可。
ch := make(chan []struct{}, 1000)
监控容量情况更是简单,len(ch)就是当前队列中未处理容量。
lithbitren
2020-04-11 12:38:59 +08:00
@susecjh
@zhs227
谢谢,懂了,还有个小问题,就是把 chan 的传递都放在 go 里面,不管是否阻塞,程序都会直接结束吗?我之前因为这个以未 chan 加上 buffer 也不是阻塞的。
lithbitren
2020-04-11 12:42:22 +08:00
用了 sync.WaitGroup 才能让程序停下来,等待死锁报错的出现。
lithbitren
2020-04-11 12:51:52 +08:00
@reus 大佬教训的是。
lithbitren
2020-04-11 13:03:22 +08:00
因为之前用 python 比较多,队列、栈、堆在标准库都是有实现的,全部仍在子线程里也会因为定容死锁会导致程序无法关闭,被 go 的一些特性迷惑了,还需要进一步理解 go 。

队列的问题姑且算是解决了,栈和堆的同步实现似乎也找不到啥资料。
yuikns
2020-04-11 13:18:54 +08:00
队列的话我自己 wrap 了一个。
https://github.com/argcv/stork/blob/master/schd/task_queue.go
如果需要监控 capacity 我可能进一步再在这个 struct 中加
yuikns
2020-04-11 13:23:26 +08:00
栈和堆其实可以也可以用一个 buffer 实现。不过没有 template 构建一个 general 的东西比较麻烦,go 自己的 pool 居然还是 interface,出入都加个类型切换,感觉有点粗糙。
shujun
2020-04-11 13:50:19 +08:00
自其实这种 自己封装一个很简单的。


ringbuffer +chan, 比自己加锁效率高
lithbitren
2020-04-12 08:53:46 +08:00
@shujun 谢谢大佬提示,ringbuffer 原理倒是没啥问题,在 golang 的具体实现中是把 chan 装进 container 的 ring 里面(还是说直接用数组就行),然后进行转圈原子操作吗?还有 buffer 大小一般怎么设定啊,其他一般语言都是号称无锁实现的,而且就算有管道机制也和 golang 的 chan 不尽相同,golang 实现还有点懵懂。
lithbitren
2020-04-12 09:31:43 +08:00
读了下 ring 的源码,除 interface 好像没啥了,其他环形实现似乎上也没啥区别的样子

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/661400

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX