我有两个方法的类型; 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
}
...
}
这是比赛条件吧?
在这种情况下使用原子存储/加载是否有意义?
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
}
...
}
答案 0 :(得分:2)
这是比赛条件吧?
如果Close()
与Add()
同时被调用,那确实是一场数据竞赛。 “种族条件”是一个更笼统的术语,原子仅阻止数据竞争,而不是所有竞争条件。并发的定义是基于https://golang.org/ref/mem的,但是如果没有协调并且是通过多个goroutine完成的,则它是并发的。
在这种情况下使用原子存储/加载是否有意义?
严格来说,这不太可能。您编写的是无数据竞争的。但是,原子是棘手的。例如,如果现在向该结构添加一个原子int64,则在x86-32机器上它将具有不进行64位对齐的数据争用,而如果您首先订购了int64,则不会。原子可能很危险,应格外小心。
或者,频道会成为一种更理想的方式
是的!您也可以使用sync.Mutex甚至sync.RWMutex。除非您是从字面上编写sync.Mutex实现,否则原子是一种优化。在第一个实现中,应始终避免使用它们。如果您有可衡量的锁争用,则可以考虑使用原子。只是要非常小心,并意识到很有可能将其弄乱。