这种 golangs 原子包的使用是否正确?
Is this use of golangs atomic package correct?
我有一个有两种方法的类型; Add
& Close
。 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
}
...
}
这是竞争条件吧?
那么在这种情况下使用原子 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 实现,否则原子是一种优化。在第一次实施时,您应该始终避免使用它们。如果您有可测量的锁争用,则可以考虑使用原子。只是要非常小心,并意识到很有可能搞砸了。
我有一个有两种方法的类型; Add
& Close
。 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
}
...
}
这是竞争条件吧?
那么在这种情况下使用原子 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 实现,否则原子是一种优化。在第一次实施时,您应该始终避免使用它们。如果您有可测量的锁争用,则可以考虑使用原子。只是要非常小心,并意识到很有可能搞砸了。