作为一个愚蠢的基本线程练习,我一直在尝试实现理发师睡觉的问题 http://en.wikipedia.org/wiki/Sleeping_barber_problem在戈兰。对于通道来说,这应该很容易,但我遇到了一个 heisenbug。也就是说,当我尝试诊断它时,问题就消失了!
考虑以下。这main()
函数将整数(或“客户”)推入shop
渠道。barber()
读到shop
剪“顾客”头发的渠道。如果我插入一个fmt.Print
声明进入customer()
函数,程序按预期运行。否则,barber()
从不给任何人剪头发。
package main
import "fmt"
func customer(id int, shop chan<- int) {
// Enter shop if seats available, otherwise leave
// fmt.Println("Uncomment this line and the program works")
if len(shop) < cap(shop) {
shop <- id
}
}
func barber(shop <-chan int) {
// Cut hair of anyone who enters the shop
for {
fmt.Println("Barber cuts hair of customer", <-shop)
}
}
func main() {
shop := make(chan int, 5) // five seats available
go barber(shop)
for i := 0; ; i++ {
customer(i, shop)
}
}
知道正在发生什么吗?
问题在于Go调度器的实现方式。当前 goroutine 只有在进行系统调用或阻塞通道操作时才能让步给其他 goroutine。fmt.Println
进行系统调用,给 goroutine 一个让出的机会。否则它就没有一个。
在实践中,这通常并不重要,但对于像这样的小问题,有时会突然出现。
另外,在通道上进行非阻塞发送的更惯用、不太活泼的方法是:
func customer(id int, shop chan<- int) {
// Enter shop if seats available, otherwise leave
select {
case shop <- id:
default:
}
}
按照您的方式,顾客最终可能会在理发店外面等待,因为当您实际发送时,len(shop)
可能已经改变了。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)