7

作为一个愚蠢的基本线程练习,我一直在尝试在 golang中实现睡眠理发师问题。有了频道,这应该很容易,但我遇到了一个 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)
    }
}

知道发生了什么吗?

4

2 回答 2

5

问题在于 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)可能已经改变了。

于 2012-04-14T04:52:33.797 回答
1

runtime.GOMAXPROCS(2)在 main 开头添加可以解决这个问题吗?

于 2012-04-14T04:48:24.453 回答