即使在 golang 中使用 sync.Mutex 时的竞争条件
Race condition even when using sync.Mutex in golang
完整代码在这里:https://play.golang.org/p/ggUoxtcv5m
go run -race main.go
说那里存在我无法解释的竞争条件。
不过,程序输出正确的最终结果。
精华:
type SafeCounter struct {
c int
sync.Mutex
}
func (c *SafeCounter) Add() {
c.Lock()
c.c++
c.Unlock()
}
var counter *SafeCounter = &SafeCounter{} // global
在增量器中使用 *SafeCounter
:
func incrementor(s string) {
for i := 0; i < 20; i++ {
x := counter
x.Add()
counter = x
}
}
incrementor
方法在 main
中生成了两次:
func main() {
go incrementor()
go incrementor()
// some other non-really-related stuff like
// using waitGroup is ommited here for problem showcase
}
所以,正如我所说,go run -race main.go
总是会说找到了竞争条件。
另外,最后的结果总是正确的(至少我已经 运行 这个程序好几次了,它总是说最终计数器是 40,这是正确的)。
但是,该程序在开始时打印了不正确的值,因此您可以得到如下内容:
Incrementor1: 0 Counter: 2
Incrementor2: 0 Counter: 3
Incrementor2: 1 Counter: 4
// ang the rest is ok
所以,那里缺少打印 1
。
有人可以解释为什么我的代码存在竞争条件吗?
读取和写入计数器指针的两行不受互斥量保护,并且由多个goroutines并发完成。
func incrementor(s string) {
for i := 0; i < 20; i++ {
x := counter // <-- this pointer read
x.Add()
counter = x // <-- races with this pointer write
}
}
您有许多竞态条件,均由竞态检测器专门指出:
x := counter // this reads the counter value without a lock
fmt.Println(&x.c)
x.Add()
counter = x // this writes the counter value without a lock
time.Sleep(time.Duration(rand.Intn(3)) * time.Millisecond)
fmt.Println(s, i, "Counter:", x.c) // this reads the c field without a lock
race #1 在 incrementor
中的 counter
值的读取和写入之间
race #2 在对 incrementor
中的 counter
值的并发写入之间
race #3 在 fmt.Println
中读取 x.c
字段和 Add
方法中增加到 x.c
之间。
完整代码在这里:https://play.golang.org/p/ggUoxtcv5m
go run -race main.go
说那里存在我无法解释的竞争条件。
不过,程序输出正确的最终结果。
精华:
type SafeCounter struct {
c int
sync.Mutex
}
func (c *SafeCounter) Add() {
c.Lock()
c.c++
c.Unlock()
}
var counter *SafeCounter = &SafeCounter{} // global
在增量器中使用 *SafeCounter
:
func incrementor(s string) {
for i := 0; i < 20; i++ {
x := counter
x.Add()
counter = x
}
}
incrementor
方法在 main
中生成了两次:
func main() {
go incrementor()
go incrementor()
// some other non-really-related stuff like
// using waitGroup is ommited here for problem showcase
}
所以,正如我所说,go run -race main.go
总是会说找到了竞争条件。
另外,最后的结果总是正确的(至少我已经 运行 这个程序好几次了,它总是说最终计数器是 40,这是正确的)。 但是,该程序在开始时打印了不正确的值,因此您可以得到如下内容:
Incrementor1: 0 Counter: 2
Incrementor2: 0 Counter: 3
Incrementor2: 1 Counter: 4
// ang the rest is ok
所以,那里缺少打印 1
。
有人可以解释为什么我的代码存在竞争条件吗?
读取和写入计数器指针的两行不受互斥量保护,并且由多个goroutines并发完成。
func incrementor(s string) {
for i := 0; i < 20; i++ {
x := counter // <-- this pointer read
x.Add()
counter = x // <-- races with this pointer write
}
}
您有许多竞态条件,均由竞态检测器专门指出:
x := counter // this reads the counter value without a lock
fmt.Println(&x.c)
x.Add()
counter = x // this writes the counter value without a lock
time.Sleep(time.Duration(rand.Intn(3)) * time.Millisecond)
fmt.Println(s, i, "Counter:", x.c) // this reads the c field without a lock
race #1 在
incrementor
中的 race #2 在对
incrementor
中的 race #3 在
fmt.Println
中读取x.c
字段和Add
方法中增加到x.c
之间。
counter
值的读取和写入之间
counter
值的并发写入之间