V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
lithbitren
V2EX  ›  Go 编程语言

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

  •  
  •   lithbitren · 2020-04-11 11:38:47 +08:00 · 5192 次点击
    这是一个创建于 1720 天前的主题,其中的信息可能已经有所发展或是发生改变。
    像 Map 类型,可以无脑加锁实现共享数据,也可以直接用 sync.Map 来实现,前者在数据规模增大时性能会陡降,后者性能则平滑稳定,和一般语言一样,相同的需求,优先选择标准库里有的一般都是最佳的。

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

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


    ringbuffer +chan, 比自己加锁效率高
    lithbitren
        19
    lithbitren  
    OP
       2020-04-12 08:53:46 +08:00
    @shujun 谢谢大佬提示,ringbuffer 原理倒是没啥问题,在 golang 的具体实现中是把 chan 装进 container 的 ring 里面(还是说直接用数组就行),然后进行转圈原子操作吗?还有 buffer 大小一般怎么设定啊,其他一般语言都是号称无锁实现的,而且就算有管道机制也和 golang 的 chan 不尽相同,golang 实现还有点懵懂。
    lithbitren
        20
    lithbitren  
    OP
       2020-04-12 09:31:43 +08:00
    读了下 ring 的源码,除 interface 好像没啥了,其他环形实现似乎上也没啥区别的样子
    fumeboy
        21
    fumeboy  
       2020-04-12 12:41:46 +08:00 via iPhone
    刚好在看 ringbuffer,还在苦恼看不懂它的作用……
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3407 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 10:56 · PVG 18:56 · LAX 02:56 · JFK 05:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.