2012-04-14 4 views
7

Als eine alberne grundlegende Threading Übung habe ich versucht, die sleeping barber problem in Golang zu implementieren. Mit Channels sollte das ganz einfach sein, aber ich bin auf einen Heisenbug gestoßen. Das heißt, wenn ich versuche, es zu diagnostizieren, verschwindet das Problem!Drucken auf stdout bewirkt, dass blockierte Goroutine ausgeführt wird?

Betrachten Sie Folgendes. Die main() Funktion drückt Ganzzahlen (oder "Kunden") auf den Kanal shop. barber() liest den shop Kanal, um "Kunden" Haare zu schneiden. Wenn ich eine fmt.Print-Anweisung in die customer()-Funktion einfügen, wird das Programm wie erwartet ausgeführt. Ansonsten schneidet barber() niemals jemandes Haar.

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

Eine Idee, was ist los?

Antwort

5

Das Problem ist die Art, wie Go's Scheduler implementiert ist. Die aktuelle goroutine kann nur dann zu anderen goroutines führen, wenn sie einen Systemaufruf oder eine blockierende Kanaloperation ausführt. fmt.Println macht einen Systemaufruf, der der Goroutine die Möglichkeit gibt, nachzugeben. Sonst hat es keinen.

In der Praxis ist das oft nicht wichtig, aber für kleine Probleme wie dieses kann es manchmal vorkommen.

Auch ein idiomatisch, weniger rassig Weg, um eine zu tun auf einem Kanal senden non-blocking ist:

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

Die Art und Weisen Sie es tun, ein Kunde warten außerhalb des Barbiers könnte am Ende Shop, da bis zu dem Zeitpunkt, an dem Sie tatsächlich den Versand vornehmen, sich len(shop) geändert haben kann.

+1

Ihre Antwort gilt, wenn nur ein Thread von der go-Laufzeit verwendet wird. Wenn GOMAXPROCS entweder über einen Laufzeitaufruf gesetzt wird, wie es bei lazy1 der Fall ist, oder wenn die Umgebungsvariable gesetzt wird, kann jede goroutine parallel zu anderen goroutines in einem separaten Thread laufen. Es könnte sich lohnen, Ihre Antwort zu erweitern, um zu zeigen, wie die Laufzeit von go auf die verfügbaren Threads multiplext. –

1

Fügt das Hinzufügen von runtime.GOMAXPROCS(2) am Anfang von Haupt dies?

+0

In der Tat, es tut. Selbst auf meiner Single-Core-Maschine ... – Jjed

+0

Auch wenn es behoben wird, ist es nicht der richtige Ansatz. –

+0

@MattJoiner Kannst du es ausarbeiten? – lazy1

Verwandte Themen