Golang Go语言中带阻塞的高性能队列最佳实践是啥?

Golang Go语言中带阻塞的高性能队列最佳实践是啥?
像 Map 类型,可以无脑加锁实现共享数据,也可以直接用 sync.Map 来实现,前者在数据规模增大时性能会陡降,后者性能则平滑稳定,和一般语言一样,相同的需求,优先选择标准库里有的一般都是最佳的。

可是对于队列来说,不管是 fifo 队列,还是栈和堆,在标准库里好像并没有并发的实现,无脑加锁倒是可以,参考 sync.Map 源码写一个理论上也不是不行,但有没有更优或更简洁的实现方法呢,最好是满足定容队列满了 put 方法和队列为空时 get 方法都自带阻塞的。

22 回复

chan

更多关于Golang Go语言中带阻塞的高性能队列最佳实践是啥?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


chan 就是你说的特性…



chan 可实现不定容的读写阻塞,但咋实现定容的写入阻塞啊?

#3 buffer ?

go 的 channel 貌似底层实现用的还是锁,github 上有第三方的 lockfree ring buffer,具体叫啥忘了,可以搜一下。

是说类似于 make(chan int 10)这种写法吗?

make(chan int, 10)这样不就是定容吗

定容的意思是固定容量吗?或者并发数?那直接设个长度不就好了吗…

chan 还有问题,不知道无法监控容量情况,而且也只是实现了 fifo,实现 filo 和优先队列似乎也挺麻烦。

思而不学

你说的这个可以参考一下 buffered chan 。申请 chan 的时候指定长度即可。
ch := make(chan []struct{}, 1000)
监控容量情况更是简单,len(ch)就是当前队列中未处理容量。



谢谢,懂了,还有个小问题,就是把 chan 的传递都放在 go 里面,不管是否阻塞,程序都会直接结束吗?我之前因为这个以未 chan 加上 buffer 也不是阻塞的。

用了 sync.WaitGroup 才能让程序停下来,等待死锁报错的出现。

大佬教训的是。

因为之前用 python 比较多,队列、栈、堆在标准库都是有实现的,全部仍在子线程里也会因为定容死锁会导致程序无法关闭,被 go 的一些特性迷惑了,还需要进一步理解 go 。

队列的问题姑且算是解决了,栈和堆的同步实现似乎也找不到啥资料。

队列的话我自己 wrap 了一个。
https://github.com/argcv/stork/blob/master/schd/task_queue.go
如果需要监控 capacity 我可能进一步再在这个 struct 中加

栈和堆其实可以也可以用一个 buffer 实现。不过没有 template 构建一个 general 的东西比较麻烦,go 自己的 pool 居然还是 interface,出入都加个类型切换,感觉有点粗糙。

自其实这种 自己封装一个很简单的。


ringbuffer +chan, 比自己加锁效率高

谢谢大佬提示,ringbuffer 原理倒是没啥问题,在 golang 的具体实现中是把 chan 装进 container 的 ring 里面(还是说直接用数组就行),然后进行转圈原子操作吗?还有 buffer 大小一般怎么设定啊,其他一般语言都是号称无锁实现的,而且就算有管道机制也和 golang 的 chan 不尽相同,golang 实现还有点懵懂。

读了下 ring 的源码,除 interface 好像没啥了,其他环形实现似乎上也没啥区别的样子

刚好在看 ringbuffer,还在苦恼看不懂它的作用……

在Golang中,实现带阻塞的高性能队列,通常会用到Go的通道(channel)机制,这是Go语言提供的一种高效、安全的并发通信方式。以下是一些最佳实践:

  1. 使用带缓冲的通道:通过指定缓冲大小,可以在队列中预先存储一定数量的元素,从而减少阻塞的发生。例如,make(chan T, bufferSize) 创建一个带缓冲的通道。

  2. 生产者-消费者模式:将生产者(写入通道)和消费者(读取通道)逻辑分离,确保它们独立运行。这有助于实现并发处理,提高队列的吞吐量。

  3. 避免死锁:确保在任何时候,至少有一个生产者正在写入数据,或至少有一个消费者正在读取数据。使用select语句可以优雅地处理通道关闭或超时等情况,防止死锁。

  4. 优雅关闭通道:在程序结束或队列不再需要时,应优雅地关闭通道。关闭通道会通知所有等待读取的goroutine不再有新数据写入,但已发送的数据仍会被读取。

  5. 监控与调优:使用Go的runtime包和pprof工具监控队列的性能,包括CPU、内存使用情况以及goroutine的状态。根据监控结果,调整缓冲大小、并发级别等参数,以达到最佳性能。

  6. 错误处理:在队列操作中处理可能的错误,如通道关闭时的发送操作、读取空通道等,确保程序的健壮性。

遵循这些最佳实践,可以构建出高效、稳定且易于维护的带阻塞的高性能队列。

回到顶部