golangs原子包的这种用法正确吗?

时间:2019-04-13 21:29:10

标签: go atomic

我有两个方法的类型; AddCloseAdd方法是同时访问的,它应检查是否曾经调用过Close

type foo struct {
  closed bool
}

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

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

这是比赛条件吧?

在这种情况下使用原子存储/加载是否有意义?

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
  }
  ...
}

1 个答案:

答案 0 :(得分:2)

  

这是比赛条件吧?

如果Close()Add()同时被调用,那确实是一场数据竞赛。 “种族条件”是一个更笼统的术语,原子仅阻止数据竞争,而不是所有竞争条件。并发的定义是基于https://golang.org/ref/mem的,但是如果没有协调并且是通过多个goroutine完成的,则它是并发的。

  

在这种情况下使用原子存储/加载是否有意义?

严格来说,这不太可能。您编写的是无数据竞争的。但是,原子是棘手的。例如,如果现在向该结构添加一个原子int64,则在x86-32机器上它将具有不进行64位对齐的数据争用,而如果您首先订购了int64,则不会。原子可能很危险,应格外小心。

  

或者,频道会成为一种更理想的方式

是的!您也可以使用sync.Mutex甚至sync.RWMutex。除非您是从字面上编写sync.Mutex实现,否则原子是一种优化。在第一个实现中,应始终避免使用它们。如果您有可衡量的锁争用,则可以考虑使用原子。只是要非常小心,并意识到很有可能将其弄乱。