2016-04-17 7 views
1

Ich habe mit dem folgenden Code aus A Tour of Go gespielt, aber ich verstehe nicht, was passiert, wenn ich einige kleinere Änderungen anwende. Der ursprüngliche Code ist diesVersuchen, goroutines zu verstehen

package main 

import (
    "fmt" 
    "time" 
) 

func say(s string) { 
    for i := 0; i < 5; i++ { 
     time.Sleep(100 * time.Millisecond) 
     fmt.Println(s) 
    } 
} 

func main() { 
    go say("world") 
    say("hello") 
} 

und produziert diese

world 
hello 
hello 
world 
world 
hello 
hello 
world 
world 
hello 

, die in Ordnung ist: fünf mal hallo, fünfmal Welt. Ich beginnt seltsam, wenn ich

say("world") 
go say("hello") 

Nun ist die Ausgabe nur

ist nennen
world 
world 
world 
world 
world 

Kein hallo auch immer. Es wird noch seltsamer mit zwei Goroutinen

Jetzt gibt es überhaupt keine Ausgabe. Als ich i < 5 zu i < 2 und

go say("world") 
say("hello") 

I

bekommen
world 
hello 
hello 

nennen ändern Was bin ich hier?

+0

https://gobyexample.com/goroutines – elithrar

Antwort

5

Im Falle von

say("world") 
go say("hello") 

Die „Welt“ Anruf vor dem „Hallo“ goroutine vervollständigen muss gestartet. Die "goodoutine" "hallo" wird nicht ausgeführt oder because main returns abgeschlossen.

Für

die goroutines laufen nicht oder vollständig, weil Zu.

Verwenden sync.WaitGroup am Austreten aus, bevor die goroutines komplette Haupt zu verhindern:

func say(wg *sync.WaitGroup, s string) { 
    defer wg.Done() 
    for i := 0; i < 5; i++ { 
    time.Sleep(100 * time.Millisecond) 
    fmt.Println(s) 
    } 
} 

func main() { 
    var wg sync.WaitGroup 
    wg.Add(2) 
    go say(&wg, "world") 
    go say(&wg, "hello") 
    wg.Wait() 
} 

playground example

1

Dies liegt daran, dass die Hauptfunktion beendet wurde.

Wenn die Hauptfunktion zurückkehrt, werden alle Goroutinen abrupt beendet, und das Programm wird beendet.

Sie fügen eine statment:

time.Sleep(100 * time.Second) 

vor Hauptfunktion Rückkehr, und alles geht gut.

Aber eine gute Praxis in Go ist die Verwendung von Kanal, der für die Kommunikation zwischen den Goroutines verwendet wird. Sie können es verwenden, um die Hauptfunktion warten zu lassen, bis Hintergrund-Goroutines beendet werden.

0

Mit

func main() { go say("world") say("hello") }

Sie erstellen zwei separate goroutines, ist eine der Hauptfunktionen Goroutine und man ist der Go sagen ("Welt"). Wenn Funktionen ausgeführt werden, springt das Programm normalerweise zu dieser Funktion, führt den gesamten Code aus und springt dann zu der Zeile, von der aus die Funktion aufgerufen wurde.

Mit goroutines springen Sie nicht innerhalb der Funktion, aber Sie starten die Goroutine in einem separaten Thread und führen die Linie direkt nach dem Anruf aus, ohne darauf zu warten.

Daher wird die Goroutine keine Zeit haben, um zu beenden, bevor die Haupt-Routine ausgeführt wird.

2

Herzlichen Glückwunsch zum Lernen Go. Als jemand Neuem ist es nett, Nebenläufigkeit zu verstehen und wie es sich von Parallelismus unterscheidet.

Concurrency
Concurrency ist wie ein Taschenspieler mit einer Hand mehrere Kugeln in der Luft jonglieren. Egal wie viele Bälle er jongliert, nur ein Ball berührt seine Hand jeden Moment.

Parallelism
Wenn der Jongleur beginnt mehr Kugeln mit der anderen Hand parallel jongliert haben wir zwei gleichzeitige Prozesse zur gleichen Zeit ausgeführt werden.

Goroutines sind groß, weil sie beide gleichzeitige und Auto-parallel, abhängig von den Rechenkernen zur Verfügung und das GOMAXPROCS Variable, Set sind.

Die Einhand Juggler
Zurück zur einhändigen, Single-entkernt, gleichzeitig Jongleur. Stellen Sie sich vor, dass er drei Bälle mit den Namen "Hallo", "Welt" und "Mars" jongliert, wobei die Hand die main Routine ist.

var balls = []string{"hello", "world", "mars"} 

func main() { 
     go say(balls[0]) 
     go say(balls[1]) 
     go say(balls[2]) 
} 

oder besser,

func main() { 
     for _, ball := range balls { 
       go say(ball) 
     } 
} 

Sobald die drei Kugeln in die Luft nacheinander geworfen, zieht sich die Gaukler einfach seine Hand sofort. Das heißt, die main Routine endet, bevor der erste geworfene Ball überhaupt auf seiner Hand landen kann. Schade, die Bälle fallen einfach auf den Boden. Schlechte Show.

Um die Bälle zurück in seine Hand zu bekommen, muss der Jongleur dafür sorgen, dass er für sie auf wartet. Das bedeutet, dass seine Hand die Bälle, die er geworfen hat, im Auge behalten und zählen kann, wenn sie landen.

Der einfachste Weg ist sync.WaitGroup zu verwenden:

import (
    "fmt" 
    "time" 
    "sync" 
) 

var balls = []string{"hello", "world", "mars"} 
var wg sync.WaitGroup 

func main() { 
     for _, ball := range balls { 
       // One ball thrown 
       wg.Add(1) 
       go func(b string) { 
         // Signals the group that this routine is done. 
         defer wg.Done() 
         // each ball hovers for 1 second 
         time.Sleep(time.Duration(1) * time.Second) 
         fmt.Println(b) 
         // wg.Done() is called before goroutine exits 
       }(ball) 
     } 

     // The juggler can do whatever he pleases while the 
     // balls are hanging in the air. 

     // This hand will come back to grab the balls after 1s. 
     wg.Wait() 
} 

WaitGroup einfach ist. Wenn eine Goroutine erzeugt wird, fügt man zu einem "Rückstandszähler" mit WaitGroup.Add(1) hinzu und ruft WaitGroup.Done() auf, um den Zähler zu verringern. Sobald der Backlog 0 wird, bedeutet das, dass alle goroutines fertig sind und WaitGroup aufhören sollte zu warten (und die Bälle greifen!).

Während die Verwendung von Kanälen für die Synchronisation in Ordnung ist, wird empfohlen, verfügbare gleichzeitige Tools zu verwenden, insbesondere wenn die Verwendung von Kanälen den Code komplexer und schwer zu verstehen macht.