V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
Sponsored by
LinkedIn
2000 个不用坐班的远程好工作在召唤你 · 弹性上班不打卡,工作和生活都能拥有
2000 个不用坐班的全球远程工作,帮助 V2EX 的小伙伴开启全新的工作方式。
Promoted by LinkedIn
hkhk366
V2EX  ›  Go 编程语言

go 语言有没有线程安全的数据类型?

  •  1
     
  •   hkhk366 · 113 天前 · 3820 次点击
    这是一个创建于 113 天前的主题,其中的信息可能已经有所发展或是发生改变。

    go 语言中都是可以并行的协程,我们为了简单下面统一称为线程安全。

    slice 和 map 都是线程不安全的,我知道 sync.map 是安全的,但是一是操作不方便比如下面这一段

    	a := sync.Map{}
    	a.Store("1", 1)
    

    就得用 store 方法才能存储,而普通 map 可以直接 map["key"]操作,这个小问题还可以勉强接受。

    最关键的问题是只提供了线程安全的 sync.map 这种,没有提供线程安全的 slice ,如果要自己一个一个加锁或者用 channel 控制非常麻烦。请问有没有别人实现好的线程安全的 slice 或者线程安全的全部数据结构 ?

    我在 github 上搜全都是别人实现的线程安全的 map 或者 queue 或者 set ,而且风格非常不统一,求有哪位大神实现了线程安全的 slice,map,queue,stack 等常见数据结构,这样风格能统一,并且还能线程安全,谢谢各位。

    我实在技术不太行,我写的话经常有并发问题,所以才来求这种线程安全的基础数据结构,我知如果用了这种线程安全的基础数据结构会导致性能下降,没关系,我的系统对性能要求没那么高。我要是有能力一个人实现上面这些,我就早全一个风格实现不来麻烦大家了。希望有大神能帮帮我,万分感谢。

    第 1 条附言  ·  113 天前
    下面有人提到 chan 就挺安全,我不明白他的意思,或许他是指用 chan 代替 slice?如果是这个意思,恐怕不行,因为 chan 只有相当于 pushright 和 popleft ,无法模拟 slice 的按下表找元素,而且 chan 不能临时增加容量。如果他是指用 chan 进行通讯然后坐到线程安全,我能找到的 slice 线程安全的代码都只有加入元素安全,还没有支持 slice 的其他操作比如按下标查找,也没有 slice 的切片等更高级的操作,我希望的是一个完整的 slice 功能,谢谢。
    22 条回复    2022-06-08 14:04:55 +08:00
    c8iter
        1
    c8iter  
       113 天前
    chan 就挺安全的
    yulon
        2
    yulon  
       113 天前
    线程安全的数据类型,只能保证单条操作是原子的,不能保证逻辑流程也是线程安全的,不仅仅是性能的问题。

    sync 包很简陋,因为这是不被推荐的方式,只能适用于某些情况,并不能一把梭。

    多线程编程本来就是很考验逻辑的,建议用 JS 。
    fds
        3
    fds  
       113 天前
    用 chan 的意思是,把对同一 slice 的操作控制在一个 goroutine 里,这样就不用考虑锁的问题。
    没有现成的是因为锁的需求场景都不一样。比如有两个 slice ,你可以用同一个锁限制他们的访问。拿到锁了,就可以对 slice 做很多放多操作。但是如果是别人包装好的,为了保证不出错,可能每次查询都要锁一次,效率太低。
    Yvette
        4
    Yvette  
       113 天前
    好久没写 Go 了,隐约记得之前解决这种问题都直接加一个 sync.Mutex ,非常粗暴但有效而且易懂
    gogorush
        5
    gogorush  
       113 天前
    Java 和 golang 的编程逻辑其实有蛮多不一样的地方 很多用这么多锁的地方感觉都在 golang 里面用起来怪怪的 这也是雪心新语言的一个必经之路吧 如果全用老的思维来处理问题 新语言的特性你也用不上了 那干嘛不直接用 Java 呢
    iosyyy
        6
    iosyyy  
       113 天前
    @fds 直接用 copy on write 的思路不就行了吗..扯这么多不还是因为没有现成的实现吗..
    keakon
        7
    keakon  
       113 天前
    你自己加个锁不就行了吗? sync.Map 单独实现出来是因为可以分片加锁,这样不同的 cpu 访问不同的分片时可以不用加锁,而其他数据结构没有这样的优化方法。
    qwqaq
        8
    qwqaq  
       113 天前 via iPhone
    最近用到的一些 Golang 处理并发问题的方法:sync.Mutex 加互斥锁保证同一个时间点只有一个操作在进行;使用 sync.Once 初始化单个实例(例如懒汉式单例模式);使用 signleflight.Group 解决并发缓存查询问题(缓存击穿问题)
    stevefan1999
        9
    stevefan1999  
       113 天前 via Android
    stevefan1999
        10
    stevefan1999  
       113 天前 via Android
    iyaozhen
        11
    iyaozhen  
       113 天前
    其实吧 你自己说技术一般,那就不要多协程操作呀。或者是不要多协程操作一个 slice
    ch2
        12
    ch2  
       113 天前
    chan 保平安,尽可能不要用锁,实在不行 sync.map 肯定够用了
    george404
        13
    george404  
       113 天前
    如果是用 slice 这些,如果是复杂的用法,你可以封成一个对象来访问啊。对象里面添加 lock 。这样你就不要每个调用的地方都放一个 RWlock 这些。
    Binb
        14
    Binb  
       112 天前
    说的挺全了:锁、chan 、sync.Map, 还是不要并发好点,简单。
    fds
        15
    fds  
       112 天前
    @iosyyy 我说的 chan 或者锁,两者都跟 copy on write 没有关系。之所以 go 官方没有楼主说的功能,是因为不需要,用别的思路不需要 slice 本身线程安全。天下没有免费的午餐,保证线程安全是要有代价的,go 希望开发者自己选择承担多少锁的时间,而不是直接无脑使用一个库。
    iosyyy
        16
    iosyyy  
       112 天前
    @fds 所以 go 语言本身使用协程希望并发都在用户态简单 然后不加一个很容易封装的 slice 说希望开发者选择吗? 站不住脚吧 同样的我举 copy on write 是指的 go 语言完全可以封装一个这种思路的对象然后 open 出来哪怕这东西很不自由 但是他很有用
    iosyyy
        17
    iosyyy  
       112 天前
    @fds 如果想不无脑建议让 go 把线程放出来 不要使用协程这种无脑的方式
    fds
        18
    fds  
       112 天前
    @iosyyy 哦,你是说 copy on write 是另一种思路,我之前没理解你的回复。copy on write 明显更难实现一些,成本更高一些。go 的设计理论是用 CSP ,就是把对同一变量的同一时间的操作限制在同一协程里,用 chan 在协程间传递命令或结果,这样对所有变量(不仅仅是 slice ,最普通的变量都不应该同时被多处读写)操作前都不用考虑锁的问题。如果是初学 go ,建议尽量往这个思路上靠拢,因为理论上是完备的,所有问题肯定是可以通过这种思路解决的。但是 go 不像一些更前卫的语言直接封掉了协程间共享变量,还是保留了锁,就是在一些特定情景下,直接用锁更直观。这就跟 go 保留了 goto 语句一样,你总是可以用别的方法避免 goto 的,但有时就是直接 goto 更清晰明了。像 sync.Map 这种封装是直到 1.9 才放出,就是因为官方认为这并不是必要的。现实总是充满各种妥协,官方为了避免一些用户自己实现出问题,还是集成了 sync.Map ,在 https://pkg.go.dev/sync#Map 文档里说了,只在特定场景中使用。

    你后面说的线程我不太理解,协程已经把线程池这个概念封装掉了,不用让每个程序员都直接处理一些底层优化。不清楚你说的无脑是什么意思。
    statumer
        19
    statumer  
       112 天前 via iPhone
    你自己加个 mutex 不就好了,数组这个数据结构太灵活了,如果你搞不懂 mutex 的话其实很容易出错。比如说你获取 index 然后删除应不应该是原子操作呢? shift 应不应该是原子操作呢?
    alexmy
        20
    alexmy  
       111 天前
    Actor 模式保命?看看这个。
    lysS
        22
    lysS  
       109 天前
    不要通过共享内存来通信
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2494 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 42ms · UTC 11:31 · PVG 19:31 · LAX 04:31 · JFK 07:31
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.