以下内容大致基于 Go 实践(第 81 页):
$ cat simple_locking_with_buffered_channels.go
package main
import(
"fmt"
"time"
)
func main(){
reap := 0; sow := 0
lock := make(chan bool,4100)
for i:=0; i<4001; i++{
go worker(i, lock, &reap)
sow += 1
}
for reap != sow {
fmt.Println("*yawn*")
time.Sleep(100 * time.Millisecond)
}
close(lock)
}
func worker(i int, lock chan bool, reap *int){
fmt.Printf("%d wants the lock\n", i)
lock <-true // we acquire the lock thusly.
fmt.Printf("%d has the lock\n", i)
time.Sleep(500 * time.Millisecond)
fmt.Printf("%d is releasing the lock\n", i)
*reap += 1
<-lock // release
}
当我运行它时,大多数时候它都会完成,但偶尔我会看到它打哈欠旋转 - 永远如此,直到它被杀死。是的,我可以添加超时逻辑,但我想知道为什么会发生这种情况。
$ ps -p `pgrep simple_locking` -o lstart,etime
STARTED ELAPSED
Sun Jul 8 11:34:59 2018 02:41
$ ps -p `pgrep simple_locking` -o lstart,etime
STARTED ELAPSED
Sun Jul 8 11:34:59 2018 03:24
它应该可以工作,那么为什么会发生奇怪的行为。在这些情况下,为什么我的收获!=播种?
~/golearn $ go version
go version go1.10.3 linux/amd64
我在一台繁忙的老式 Linux 笔记本电脑上运行这个程序,我很困惑为什么它会间歇性旋转?提前致谢!
https://play.golang.org/p/BJwAmRf1OXB
更新:1
我更改了代码以使用互斥体(或者我认为......):
package main
import(
"fmt"
"time"
"sync"
)
var mutex sync.Mutex
func main(){
reap := 0; sow := 0
lock := make(chan bool,400)
for i:=0; i<389; i++{
go worker(i, lock, &reap)
sow += 1
}
time.Sleep(100 * time.Millisecond)
for reap != sow {
fmt.Println("*yawn*")
time.Sleep(100 * time.Millisecond)
}
close(lock)
}
func worker(i int, lock chan bool, reap *int){
fmt.Printf("%d wants the lock\n", i)
lock <-true // we acquire the lock thusly.
fmt.Printf("%d has the lock\n", i)
time.Sleep(500 * time.Millisecond)
fmt.Printf("%d is releasing the lock\n", i)
mutex.Lock()
*reap += 1
mutex.Unlock()
<-lock // release
}
这是正确的方法吗,因为 go run --race
仍然显示 WARNING: DATA RACE
?
*更新3:*
在尝试了 go 的原子计数器(需要增量之间的延迟)之后,我最终使用了互斥体。我学到的是:即使是阅读(而不是写作)也会让它提示竞争条件。因此,在这里,我将调用包装在一个使用互斥体进行读取的函数调用中,并且它清除了 --race 测试:
$ cat improv.go
package main
import(
"fmt"
"time"
"sync"
)
var mutex sync.Mutex
func main(){
sow := 0
reap := 0
lock := make(chan bool,40)
for i:=0; i<38; i++{
go worker(i, lock, &reap)
sow += 1
}
time.Sleep(100 * time.Millisecond)
//for get_counter(&reap) != get_counter(&sow) {
for get_counter(&reap) != sow {
fmt.Println("*yawn*")
time.Sleep(100 * time.Millisecond)
}
}
func worker(i int, lock chan bool, reap *int){
fmt.Printf("%d wants the lock\n", i)
lock <-true // we acquire the lock thusly.
fmt.Printf("%d has the lock\n", i)
time.Sleep(500 * time.Millisecond)
fmt.Printf("%d is releasing the lock\n", i)
mutex.Lock()
defer mutex.Unlock()
*reap += 1
<-lock // release
}
func get_counter(counter *int) int {
mutex.Lock()
defer mutex.Unlock()
return *counter
}
$ go run --race improv.go >/dev/null
最佳答案
您的代码存在数据争用(请参阅 Go Data Race Detector )。因此,您的结果是未定义的。
$ go run -race racer.go > /dev/null
==================
WARNING: DATA RACE
Read at 0x00c000086010 by goroutine 7:
main.worker()
/home/peter/gopath/src/so/racer.go:29 +0x1c9
Previous write at 0x00c000086010 by goroutine 661:
main.worker()
/home/peter/gopath/src/so/racer.go:29 +0x1e2
Goroutine 7 (running) created at:
main.main()
/home/peter/gopath/src/so/racer.go:13 +0xb0
Goroutine 661 (finished) created at:
main.main()
/home/peter/gopath/src/so/racer.go:13 +0xb0
==================
==================
WARNING: DATA RACE
Read at 0x00c000086010 by goroutine 688:
main.worker()
/home/peter/gopath/src/so/racer.go:29 +0x1c9
Previous write at 0x00c000086010 by goroutine 661:
main.worker()
/home/peter/gopath/src/so/racer.go:29 +0x1e2
Goroutine 688 (running) created at:
main.main()
/home/peter/gopath/src/so/racer.go:13 +0xb0
Goroutine 661 (finished) created at:
main.main()
/home/peter/gopath/src/so/racer.go:13 +0xb0
==================
Found 2 data race(s)
exit status 66
$
您的 update-1 代码存在数据争用。因此,您的结果是未定义的。
$ go run -race racer.go >/dev/null
==================
WARNING: DATA RACE
Read at 0x00c000088010 by main goroutine:
main.main()
/home/peter/src/so/racer.go:20 +0x136
Previous write at 0x00c000088010 by goroutine 397:
main.worker()
/home/peter/src/so/racer.go:34 +0x1f2
Goroutine 397 (finished) created at:
main.main()
/home/peter/src/so/racer.go:16 +0xb0
==================
Found 1 data race(s)
exit status 66
$
关于go - 通过缓冲 channel 旋转进行简单锁定,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51234951/