2014-08-28 21 views
6

Stavo seguendo il Go Tour e sono un po 'bloccato quando si tratta di goroutine. Capisco che sono molto leggeri e che ogni volta che un blocchi goroutine, un altro comincerò ma non riesco a ottenere la mia testa intorno a come questo esempio funziona realmente:Come funzionano le goroutine?

package main 

import (
    "fmt" 
    "time" 
) 

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

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

Playground

Capisco che un goroutine viene avviato per la funzione say con l'argomento "world", ma per quanto ho capito dovrebbe stampare "world" cinque volte e "ciao" una volta. Tuttavia non capisco il motivo per cui l'uscita è come è:

hello 
world 
hello 
world 
hello 
world 
hello 
world 
hello 

Dalla mia comprensione limitata di fili da altre lingue l'uscita dovrebbe essere stato qualcosa di simile:

hello 
world 
world 
world 
world 
world 

o come questo:

world 
world 
world 
hello 
world 
world 

Perché la seconda riga viene eseguita anche cinque volte? Qualcosa sotto l'istruzione go classifica come parte della routine go?

Anche la diapositiva successiva mostra qualcosa che non posso ottenere la mia testa rotonda di nuovo:

package main 

import "fmt" 

func sum(a []int, c chan int) { 
    sum := 0 
    for _, v := range a { 
     sum += v 
    } 
    c <- sum // send sum to c 
} 

func main() { 
    a := []int{7, 2, 8, -9, 4, 0} 

    c := make(chan int) 
    go sum(a[:len(a)/2], c) 
    go sum(a[len(a)/2:], c) 
    x, y := <-c, <-c // receive from c 

    fmt.Println(x, y, x+y) 
} 

Playground

Un goroutine viene avviato per la seconda metà della fetta e poi un altro per la prima parte della sezione, tuttavia i valori x e sono stati assegnati a due valori diversi. Per come la vedo io, la funzione sum invierà la somma al canale c e quindi il prossimo sum invierà la somma allo stesso canale c, quindi come possono essere assegnate alle due variabili due valori diversi? Il canale c non dovrebbe contenere un solo valore sum?

Apprezzo che questa è una domanda piuttosto lunga, ma non sono riuscito a trovare la risposta a queste domande.

risposta

6

Perché la seconda riga viene eseguita anche 5 volte?

La seconda riga stampa hello ogni secondo 5 volte nel thread main().
Ma contemporaneamente la prima riga go say("world") stamperà anche il mondo ogni cinque volte cinque volte in una goroutine separata.
Il Sleep assicura che ogni routine produca, consentendo all'altro di riprendere.

quindi l'uscita:

hello 
world 
hello 
world 
hello 
world 
hello 
world 
hello 

Il modo di vedere la funzione somma invierà è somma di canale C e quindi la successiva somma invierà è somma allo stesso canale c così come possono essere assegnate alle due variabili due valori diversi?

Poiché ogni invio verrà bloccato su c fino a leggere il canale c.
Poiché ci sono due in scrittura a c, è necessario leggere:

x, y := <-c, <-c // receive from c twice. 

Il Assignement section of Golang Spec consente per l'assegnazione di tuple se:

il numero di operandi a sinistra deve essere uguale al numero di espressioni a destra, ognuno dei quali deve essere a valore singolo e l'espressione nth a destra è assegnata all'enesimo operando a sinistra.

+0

Oh. Penso di aver capito. Il principale è di per sé una routine di go come hai detto tu. Il saluto viene stampato perché il principale viene bloccato dal sonno all'interno della routine di go. Perché ogni volta che una routine viene bloccata, una nuova avvia le ripetizioni principali per 5 volte, ma allo stesso tempo la funzione si ripete 5 volte. Hello viene stampato per primo perché il codice salta sopra la routine in attesa che si addormenti. – Bula

+0

Anche questa è un'altra domanda riguardante il mio secondo esempio. Diciamo che ci sono milioni di chiamate per le funzioni di somma con il go preposto ad esso. Se il programma non arriva alla linea che blocca il programma prima che la somma finisca di calcolare, quel valore andrebbe perso proprio? – Bula

+0

@Bula scusa per la risposta in ritardo. Sì, main() è una goroutine di per sé, e produrrà in 'Sleep()'. – VonC

1

Per la prima funzione è necessario visualizzare i valori nello stile VonC presentato. Il motivo hello stampa 5 volte è perché la funzione say stampa le cose 5 volte. Immagina il programma senza la goroutine. Penso che non garantisce che otterrete hello e world perfettamente intervallati, ma potrei sbagliarmi.

La ragione funziona il canale è:

  1. Golang ti permette di fare assegnamento multiplo come VonC menziona
  2. Canali empty out, cioè quando si assegna c-x si rimuove la prima somma che è stata passata nel canale e quando assegna c a passa nel secondo valore (di nuovo penso che l'ordine non è una garanzia come x potrebbe avere il primo semestre e la seconda metà o viceversa.

Se immaginate i canali come una sorta di coda, penso che abbia più senso. La somma delle goroutine spinge i valori sulla coda e gli assegnamenti popolano i valori in sequenza.