2012-04-14 13 views
7

Jako głupie podstawowe ćwiczenie gwintowania, próbowałem wdrożyć sleeping barber problem w języku golang. Z kanałami to powinno być całkiem łatwe, ale wpadłem na heisenbug. To znaczy, kiedy próbuję ją zdiagnozować, problem znika!Drukowanie na standardowe wyjście powoduje zablokowanie goroutiny?

Należy rozważyć następujące kwestie. Funkcja main() przesyła liczby całkowite (lub "klienci") na kanał shop. barber() czyta kanał shop, aby wyciąć włosy "klientów". Jeśli wstawię instrukcję fmt.Print do funkcji customer(), program działa zgodnie z oczekiwaniami. W przeciwnym razie, barber() nigdy nie obcina niczyich włosów.

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

Każdy pomysł, co się dzieje?

Odpowiedz

5

Problem polega na tym, w jaki sposób wdrażany jest harmonogram Go. Obecna goroutyna może ustępować innym goryntom tylko wtedy, gdy wykonuje wywołanie systemowe lub blokuje działanie kanału. fmt.Println wykonuje wywołanie systemowe, dając goroutine okazję do otrzymania. W przeciwnym razie go nie ma.

W praktyce nie ma to większego znaczenia, ale w przypadku niewielkich problemów, takich jak ten, czasami może się pojawić.

Również bardziej idiomatyczne, mniej pikantny sposób robi non-blocking wysłać na kanale:

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

Sposób, w jaki to robią, klient może skończyć się czeka poza fryzjera sklep, ponieważ do czasu, w którym faktycznie wysyłasz, len(shop) mogła ulec zmianie.

+1

Twoja odpowiedź ma zastosowanie, jeśli w runtime go używany jest tylko jeden wątek. Ustawienie GOMAXPROCS poprzez wywołanie runtime podobnie jak lazy1 mówi, lub ustawienie zmiennej środowiskowej pozwoli także uruchomić goroutine równolegle do innych goroutines w osobnym wątku. Być może warto rozszerzyć swoją odpowiedź, aby odzwierciedlić, w jaki sposób jego środowisko wykonawcze multipleksuje goroutiny w dostępnych wątkach. –

1

Czy dodanie runtime.GOMAXPROCS(2) na początku głównej rozwiązuje to?

+0

W rzeczywistości to robi. Nawet na mojej jednordzeniowej maszynie ... – Jjed

+0

Nawet jeśli to naprawi, to nie jest właściwe podejście. –

+0

@MattJoiner Czy możesz rozwinąć? – lazy1