【问题标题】:Sharing a slice across a few different goroutines在几个不同的 goroutine 之间共享一个切片
【发布时间】:2016-11-12 07:25:49
【问题描述】:

鉴于我有一部分用户类型的结构

Users := make([]User)

我正在监听 TCP 连接,当一个用户连接时,我正在向这个切片添加一个新用户。

我这样做的方法是设置一个 NewUsers 频道​​

NewUsers := make(chan User)

在新的 TCP 连接后,用户被发送到此通道,并且中心函数等待用户到达以将其添加到用户切片。

但现在我希望多个子系统(包/函数)使用此用户列表。一个函数可能只是想接收用户列表,而另一个函数可能想向每个用户广播消息,或者只是匹配某个条件的用户。

多个函数(可能从不同的 goroutine 执行)如何安全地访问用户列表。我看到了两种可能的方法:

  1. 需要访问此列表的每个子系统都需要自己的 AddUser 频道并维护自己的用户部分,并且需要将新用户广播到这些频道中的每一个。
  2. 使用互斥锁阻止访问

选项 1 似乎非常复杂,会产生相当多的重复,但我的理解是,如果您尝试坚持“通过通信共享内存”的口头禅,最好避免使用互斥锁。

【问题讨论】:

  • 某人可能有不同的想法,但我认为如果您将目标视为共享用户,Mutex(或RWLock,类似于互斥体,但允许多个并发读者)是自然映射到的。如果一个 goroutine 自然地“拥有”用户列表,那么通道可能更有意义,但正如所要求的那样,它会带锁。
  • @twotwotwo 很好的回答,谢谢。

标签: go goroutine


【解决方案1】:

互斥锁方法是解决该问题的最佳、最安全和最易于管理的方法,而且速度最快。

Channel 内部是复杂的野兽,比 rwmutex 保护的 map/slice 慢得多。

【讨论】:

    【解决方案2】:

    在并发活动之间共享数据的惯用 Go 方式总结如下:

    不要通过共享内存进行通信;相反,通过 交流。

    例如,Andrew Gerrand blogged about this

    不必过于复杂;您可以考虑设计内部微服务,使用带有通道的 goroutines 来表达。

    在您的情况下,这可能意味着设计一个服务元素来包含用户列表的主副本。

    Go/CSP 策略的主要优势在于

    • 并发是一种设计选择,以及您设计的其他方面
    • 只需要本地知识来理解并发行为:这是因为 goroutine 本身可以由内部 goroutines 组成,如果需要,这将一直适用。理解高级 goroutine 的外部行为仅取决于其接口,而不是隐藏的内部。

    但是...

    有时安全共享的数据结构(受互斥体保护)现在就足够了。然后可能会争辩说,goroutines 和通道的额外复杂性是不必要的。

    安全共享的列表数据结构是您会发现有几个人作为开源 API 提供的东西。 (我自己也有一个 - 请参阅 runtemplate 中的内置插件)。

    【讨论】:

    • 很好的答案!内部微服务方法是有道理的,但对于我想要分享的每一点信息都必须开发非常精细的服务也很可怕。我还不确定如何调和这一点。为每个通信流创建一个通道似乎也需要很多工作,即使它是惯用的。我对此很陌生,我真的只需要了解 Go 想要我如何构建它。也许我会随着时间的推移到达那里。
    • 渠道不应该吓到你——我们也处理值和函数值。请记住,通道很像 Unix 管道——一个有两端的管道。流程通常是一种方式(但 Go 通道不限于此)。如有疑问,请绘制通道布线图,并将其视为数字硬件设计或微服务网络,以更容易者为准。
    猜你喜欢
    • 2011-05-08
    • 1970-01-01
    • 2012-11-14
    • 2019-06-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-14
    • 1970-01-01
    相关资源
    最近更新 更多