[英]Data race when GOMAXPROCS=1
I'm trying to understand one of the Golang typical data races where access to an unprotected global variable from multiple goroutines may cause a race condition: 我正在尝试理解Golang 典型数据竞赛之一 ,从多个goroutine访问未受保护的全局变量可能会导致竞争条件:
var service map[string]net.Addr
func RegisterService(name string, addr net.Addr) {
service[name] = addr
}
func LookupService(name string) net.Addr {
return service[name]
}
It goes on to say that we can solve this by protecting it with a mutex: 接下来我们可以通过使用互斥锁来保护它来解决这个问题:
var (
service map[string]net.Addr
serviceMu sync.Mutex
)
func RegisterService(name string, addr net.Addr) {
serviceMu.Lock()
defer serviceMu.Unlock()
service[name] = addr
}
func LookupService(name string) net.Addr {
serviceMu.Lock()
defer serviceMu.Unlock()
return service[name]
}
So far, so good. 到现在为止还挺好。 What's confusing me is this: 令我困惑的是这个:
The accepted answer to this question suggests that a CPU-bound goroutine will any starve other goroutines that have been multiplexed onto the same OS thread (unless we explicitly yield with runtime.Gosched()
). 对这个问题的接受答案表明,一个CPU绑定的goroutine会将多路复用到同一个OS线程上的其他goroutine(除非我们用runtime.Gosched()
明确地产生)。 Which makes sense. 这是有道理的。
To me, the RegisterService()
and LookupService()
functions above look to be CPU bound, as there's no IO and no yield. 对我来说,上面的RegisterService()
和LookupService()
函数看起来是CPU绑定的,因为没有IO和没有yield。 Is this correct? 这个对吗?
If it is, and if GOMAXPROCS is set to 1 , then is a mutex in the above example still strictly necessary? 如果是,如果GOMAXPROCS设置为1 ,那么上面例子中的互斥是否仍然是绝对必要的? Doesn't the fact that the goroutines are CPU-bound at the point where race conditions might occur take care of it? 在竞争条件可能发生的时候,goroutines是否受CPU约束这一事实不会照顾它吗?
Even if it does, I presume that in real life using a mutex is here is still a good idea as we may not be able to guarantee what GOMAXPROCS is set to. 即使它确实如此,我也认为在现实生活中使用互斥锁仍然是一个好主意,因为我们可能无法保证GOMAXPROCS的设置。 Are there another reasons? 还有其他原因吗?
As FUZxxl and Nick Craig-Wood noted, current behavior of goroutines is implementation-specific. 正如FUZxxl和Nick Craig-Wood所指出的,goroutines的当前行为是特定于实现的。 So, maybe, read or write to map can yield. 所以,也许,读取或写入地图可以产生。 Considering that maps are not thread safe , mutex or another syncronization is required for correct concurrent access. 考虑到映射不是线程安全的 ,正确的并发访问需要互斥或其他同步。
As alternative to mutex, you can spawn a goroutine, that performs all operations on your map and talk to it with channels: 作为互斥锁的替代方案,您可以生成一个goroutine,它可以在地图上执行所有操作并通过通道与它通信:
type writereq struct {
key string
value net.Addr
reply chan struct{}
}
type readreq struct {
key string
reply chan net.Addr
}
var service map[string]net.Addr
var reads = make(chan readreq)
var writes = make(chan writereq)
func RegisterService(name string, addr net.Addr) {
w := writereq{name, addr, make(chan struct{})}
writes <- w
return <-w.reply // return after registration confirmation
}
func LookupService(name string) net.Addr {
r := readreq{name, make(chan net.Addr)}
reads <- r
return <-r.reply
}
func serveRegistry() {
for {
select {
case r := <-reads:
r.reply <- service[r.name]
case w := <-writes:
service[w.name] = w.addr
w.reply <- struct{}
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.