【问题标题】:Is this thread-safe with concurrent access?这是线程安全的并发访问吗?
【发布时间】:2016-08-15 06:28:12
【问题描述】:

我有带有count 属性的结构需要线程安全访问。

我知道可以使用sync.Mutexsync.RWMutex 来完成。但我不确定这样是否可以:

type Status struct {
    count uint32

    attr1 string
    attr2 string
}

func (s *Status) Get() uint32 {
    return atomic.LoadUint32(&s.count)
}

func (s *Status) Add(n uint32) {
    atomic.AddUint32(&s.count, n)
}

func (s *Status) Reset(n uint32) {
    atomic.StoreUint32(&s.count, n)
}

谢谢。

编辑:

我很困惑直接访问字段s.count 是不安全的。但是atomic.LoadUint32(&s.count) 安全吗?

【问题讨论】:

  • 访问任何语言的任何字段都是不安全的——除了一些纯函数式语言,如 Erlang 和 Elixir 或 Haskell 或一些具有复杂类型系统的语言,它们将并发视为其类型系统的一部分,如 Pony 或一些像 Rust 这样的“提供”意味着使并发访问安全的语言。因此,在使用常用/主流编程语言时,您应该始终考虑几乎所有并发不安全的事情。

标签: go concurrency synchronization thread-safety


【解决方案1】:

是的,如果只有这 3 个方法访问 count 字段,那么您的解决方案可以安全地从多个 goroutine 中同时使用。

但是要知道,Status.Get() 返回的值在您尝试使用它时可能已经“过时”了。例如:

s := &Status{}

if s.Get() == 3 {
    fmt.Println(s.Get()) // This may or may not print 3
}

// Or
c := s.Get()
fmt.Println(c == s.Get()) // This may or may not print true

上述示例可能会或可能不会打印3true,因为第二次调用s.Get() 之前可能会在另一个goroutine 中再次调用s.Add()

如果您需要保证在您对其执行进一步计算时没有其他人修改或访问Status.count 字段的值,那么sync.Mutexsync.RWMutex 是可行的方法,因为您可以锁定count 字段,同时完成计算。

编辑:回答您的编辑:

直接访问s.count 是不安全的,atomic.LoadUint32(&s.count) 是安全的。这样做的原因是因为如果 goroutine #1 调用 s.Add(),而 goroutine #2 尝试访问 s.count,则无法保证 goroutine #2 会看到 #1 所做的更改。在 #2 中,您可能只会看到 s.count 的缓存版本。

如果没有显式同步,您无法保证观察到另一个 goroutine 中对变量所做的更改。直接访问s.count 是非同步访问。使用通道或其他同步原语(例如syncsync/atomic 包)可确保对s.count 的序列化访问,因此这些解决方案将始终看到当前的更新值。

详情请看这篇文章:The Go Memory Model

【讨论】:

    猜你喜欢
    • 2013-10-12
    • 1970-01-01
    • 1970-01-01
    • 2015-10-13
    • 1970-01-01
    • 1970-01-01
    • 2011-08-03
    • 1970-01-01
    相关资源
    最近更新 更多