这种golangs原子包的使用是否正确?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了这种golangs原子包的使用是否正确?相关的知识,希望对你有一定的参考价值。

我有两种方法的类型; AddClose。同时访问Add方法,它应检查是否曾调用过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
  }
  ...
}
答案

这是一场竞争条件吧?

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

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

严格来说,它不太可能有意义。你写的是数据竞争免费。然而,原子是棘手的。例如,如果你现在向这个结构添加了一个原子int64,它将在x86-32机器上有数据条,而不是64位对齐,而如果你先订购int64,它就不会。原子药可能很危险,应谨慎使用。

或者一个频道是一种更为自我的方式来做到这一点

是!您还可以使用sync.Mutex甚至sync.RWMutex。除非您真正编写sync.Mutex实现,否则Atomics是一种优化。您应该始终在第一个实现中避免它们。如果您有可测量的锁争用,那么您可以考虑使用原子。只是要非常小心,并意识到很有可能弄乱它。

以上是关于这种golangs原子包的使用是否正确?的主要内容,如果未能解决你的问题,请参考以下文章

Golang写时复制是否是原子性的?

Golang写时复制是否是原子性的?

Golang内存模型

Golang如何正确的停止Ticker

golang goroutine例子[golang并发代码片段]

这种用法不正确吗?