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

怎么实现一个并发安全的,对写多读少友好的 go 数据结构?

  •  
  •   wencan · 2022-11-06 18:11:46 +08:00 · 2178 次点击
    这是一个创建于 508 天前的主题,其中的信息可能已经有所发展或是发生改变。
    实现过一个并发安全的 slice ,但只支持 append 操作。见: https://github.com/wencan/gox

    现在需要一个支持删除的

    sync.Map 支持 store 和 delete ,但写多了就会经常加锁

    其中一个使用场景如下:
    每接受一个请求,把该请求添加到集合。
    请求处理完,从集合删除请求。
    服务退出时,检查集合里还有哪些请求。

    求思路
    第 1 条附言  ·  2022-11-06 20:55:55 +08:00
    13 条回复    2022-11-07 16:56:56 +08:00
    bruce0
        1
    bruce0  
       2022-11-06 19:02:55 +08:00
    并发安全, 要么加锁,要么 channel 吧, sync.map 适合读多写少的场景, 写多读少, 用 rwlook 呢, 再加个 分片, 对 key 取值, 分散到不同的 key 中, 减小锁的粒度
    fds
        2
    fds  
       2022-11-06 20:11:10 +08:00
    你确定你写的 slice 是并发安全的么?我看这里的讨论
    https://groups.google.com/g/golang-nuts/c/cQdZJMQSxTE/m/K_IrQfoZBQAJ
    - any global variables. Even reading an 'int' while some other goroutine is updating it is unsafe; it may give undefined behavior.
    在你用新 arrays 覆盖旧 arrays 的瞬间,有没有可能别的 goroutine 读到一个错误的地址呢?

    你说的应用场景我觉得 buffered channel 就可以吧,比如设置大小为 1000 ,有 10 个 worker 从里面读请求。如果要退出,可以另外启动一个 goroutine 从里面把剩下的 log 下来。
    lysS
        3
    lysS  
       2022-11-06 20:50:24 +08:00
    @fds 。。。就连一个 int 也不是并发安全的,OP 说他自己封装了一个
    wencan
        4
    wencan  
    OP
       2022-11-06 20:53:32 +08:00
    @fds
    根据你的描述,我找到了 Slice 的 Append 方法 https://github.com/wencan/gox/blob/main/xsync/slice.go#L183
    Append 下面的 Grow 段是加锁的
    覆盖是通过 sync.Value 操作的
    因此不存在你说的情况
    不知道我有没有理解错你的意思
    lysS
        5
    lysS  
       2022-11-06 20:58:10 +08:00
    @wencan 你那个 grow 中的

    array := new([8]atomic.Value)
    tail = array[:]

    有啥意义?。。。
    wencan
        6
    wencan  
    OP
       2022-11-07 07:16:23 +08:00
    @lysS 新增的空间
    你再看 Grow 函数下面那段
    lysS
        7
    lysS  
       2022-11-07 09:48:48 +08:00
    @wencan 确实没啥用啊,和直接 make 一个有啥区别?
    wencan
        8
    wencan  
    OP
       2022-11-07 09:58:12 +08:00
    @lysS 以朋友的想法,应该怎么实现一个并发安全的 slice ?最好还支持 delete
    fds
        9
    fds  
       2022-11-07 10:22:47 +08:00
    @wencan 哦,对,是我没看到下面 Slice 的具体实现,只看了最上面的 comment 就下结论了,太草率。

    不过我觉得这样套很多 atomic 只是单元素的读性能会好,如果批量读写大量数据就不好说了,应用场景比较特殊。一般用 go 写的应用,只要锁的粒度不太大,应该不会成为瓶颈吧?网络请求、磁盘 IO 的时间比这都大多了。
    yuancoder
        10
    yuancoder  
       2022-11-07 11:34:13 +08:00
    既然是写多读少,每个协程都使用自己的 slice , 读的时候再加锁
    lysS
        11
    lysS  
       2022-11-07 12:33:20 +08:00
    @wencan 我是单说你这个代码。
    lysS
        12
    lysS  
       2022-11-07 12:39:25 +08:00
    就实现来说,数组需要 delete 本身就是不太合理的,你可能需要 link/heap/map? 并发安全而言,就直接加锁吧,锁和原子变量性能上其实没多大差别; free-lock 其实比较鸡肋
    wencan
        13
    wencan  
    OP
       2022-11-07 16:56:56 +08:00
    @lysS
    slice 只是个名字而已,没必要纠结“slice” delete 操作是否合理。
    最终实现,应该不会叫 slice 。到时具体看底层数据结构,或者另外取个跟底层数据结构无关的名字。
    根据基准测试结果,基于 mutex 和基于原子的 free-lock 操作,两者性能差距还是很明显。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3233 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 12:16 · PVG 20:16 · LAX 05:16 · JFK 08:16
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.