문제

As a silly basic threading exercise, I've been trying to implement the sleeping barber problem in golang. With channels this should be quite easy, but I've run into a heisenbug. That is, when I try to diagnose it, the problem disappears!

Consider the following. The main() function pushes integers (or "customers") onto the shop channel. barber() reads the shop channel to cut "customers'" hair. If I insert a fmt.Print statement into the customer() function, the program runs as expected. Otherwise, barber() never cuts anyone's hair.

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)
    }
}

Any idea what's afoot?

도움이 되었습니까?

해결책

The problem is the way Go's scheduler is implemented. The current goroutine can yield to other goroutines only when it makes a system call or a blocking channel operation. fmt.Println makes a system call, giving the goroutine an opportunity to yield. Otherwise it doesn't have one.

In practice this doesn't often matter, but for small problems like this it can sometimes crop up.

Also, a more idiomatic, less racy way of doing a non-blocking send on a channel is:

func customer(id int, shop chan<- int) {
    // Enter shop if seats available, otherwise leave
    select {
    case shop <- id:
    default:
    }
}

The way you're doing it, a customer could end up waiting outside of the barber shop since by the time you actually do the send, len(shop) may have changed.

다른 팁

Does adding runtime.GOMAXPROCS(2) at the beginning of main solves this?

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top