这种 go​​langs 原子包的使用是否正确?

Is this use of golangs atomic package correct?

我有一个有两种方法的类型; Add & CloseAdd 方法被并发访问,它应该检查 Close 是否被调用过。

type foo struct {
  closed bool
}

func (f *foo) Close() error {
 f.closed = true
  ...
}

func (f *foo) Add(...) error {
  if f.closed {
    return ErrClosed
  }
  ...
}

这是竞争条件吧?

那么在这种情况下使用原子 store/load 有意义吗?

type foo struct {
  closed int32
}

func (f *foo) Close() error {
  atomic.StoreInt32(&f.closed, 1)
  ...
}

func (f *foo) Add(...) error {
  if atomic.LoadInt32(&f.closed) == 1 {
    return ErrClosed
  }
  ...
}

或者频道是一种更惯用的方式来做到这一点:

type foo struct {
  closed chan struct{}
}

func NewFoo() *foo {
  return &foo{make(chan struct{})}
}

func (f *foo) isClosed() bool {
    select {
    case <-f.closed:
        return true
    default:
    }
    return false
}

func (f *foo) Close() error {
  close(f.closed)
  ...
}

func (f *foo) Add(...) error {
  if f.isClosed() {
    return ErrClosed
  }
  ...
}

编辑:

谢谢你的评论,我就这样结束了。

type foo struct {
  closed bool
  closeLock sync.RWMutex
}

func (f *foo) Close() error {
  f.closeLock.Lock()
  defer f.closeLock.Unlock()
  f.closed = true
  ...
}

func (f *foo) Add(...) error {
  f.closeLock.RLock()
  defer f.closeLock.RUnlock()

  if f.closed {
    return ErrClosed
  }
  ...
}

This is a race condition right?

如果 Close()Add() 同时调用,则确实是数据竞争。 "Race condition" 是一个更笼统的术语,原子只能防止数据竞争,而不是所有竞争条件。 concurrently 的定义基于 https://golang.org/ref/mem 但如果没有协调并且它是由多个 goroutine 完成的,它是并发的。

So would using an atomic store/load in this situation make sense?

严格来说,这不太可能有意义。您所写的是无数据竞争的。然而,原子是棘手的。例如,如果您现在向此结构添加一个原子 int64,它会在 x86-32 机器上出现数据争用,因为它不是 64 位对齐的,而如果您先订购 int64,它就不会。原子可能很危险,应格外小心使用。

Or would a channel be a more idomatic way to do this

是的!您还可以使用 sync.Mutex 甚至 sync.RWMutex。除非你真的在写 sync.Mutex 实现,否则原子是一种优化。在第一次实施时,您应该始终避免使用它们。如果您有可测量的锁争用,则可以考虑使用原子。只是要非常小心,并意识到很有可能搞砸了。