2015-02-03 14 views
9

Il seguente codice viene eseguito perfettamente bene:goroutine non viene eseguito se time.sleep incluso

package main 

import (
    "fmt" 
) 

func my_func(c chan int){ 
    fmt.Println(<-c) 
} 

func main(){ 
    c := make(chan int) 
    go my_func(c) 

    c<-3 
} 

playgound_1

Tuttavia se cambio

c<-3 

a

time.Sleep(time.Second) 
c<-3 

playground_2

Il mio codice non viene eseguito.

Il mio istinto è che in qualche modo main ritorna prima che l'my_func termini l'esecuzione, ma sembra che l'aggiunta di una pausa non dovrebbe avere alcun effetto. Sono totalmente perso su questo semplice esempio, cosa sta succedendo qui?

risposta

12

Quando la funzione main termina, il programma termina con esso. Non aspetta che le altre goroutine finiscano.

Citando dalla Go Language Specification: Program Execution:

L'esecuzione del programma inizia inizializzando il pacchetto principale e quindi invocando la funzione main. Al termine di tale chiamata, il programma termina. Non attende altre goreutine (non main) da completare.

Così semplicemente quando la funzione main riesce inviando il valore sul canale, il programma potrebbe risolvere immediatamente, prima che l'altro goroutine la possibilità di stampare il valore ricevuto alla console.

Se si vuole assicurarsi che il valore viene stampato sulla console, è necessario sincronizzarlo con l'evento di uscire dalla funzione main:

Esempio con un canale "fatto" (provarlo su Go Playground) :

func my_func(c, done chan int) { 
    fmt.Println(<-c) 
    done <- 1 
} 

func main() { 
    c := make(chan int) 
    done := make(chan int) 
    go my_func(c, done) 

    time.Sleep(time.Second) 
    c <- 3 
    <-done 
} 

da done è anche un canale senza buffer, ricevendo da esso al termine della funzione main deve attendere l'invio di un valore sul canale done, che avviene dopo il valore inviato sul canale c è stato ricevuto e stampato sulla console.

Spiegazione per le corse apparentemente non deterministici:

Goroutines può o non può essere eseguito in parallelo allo stesso tempo. La sincronizzazione garantisce che determinati eventi si verifichino prima di altri eventi. Questa è l'unica garanzia che ottieni e l'unica cosa su cui devi fare affidamento. 2 esempi di questo accade prima:

  • Il go affermazione che inizia una nuova goroutine avviene prima che inizi l'esecuzione del goroutine.
  • Un'invio su un canale si verifica prima del completamento della ricezione corrispondente da quel canale.

Per maggiori dettagli leggere The Go Memory Model.

Torna al tuo esempio:

Un riceve da un canale senza buffer avviene prima l'invio su quel canale completa.

Quindi l'unica garanzia che si ottiene è che la goroutine che corre my_func() riceverà il valore del canale c inviato dal main(). Ma una volta ricevuto il valore, la funzione mainpuò continuare con ma poiché non ci sono più istruzioni dopo l'invio, termina semplicemente - insieme al programma. Sia la non main goroutine avrà tempo o possibilità stamparlo con fmt.Println() è non definito.

+2

Puoi spiegare perché nel mio primo esempio 'my_func' finisce prima dei main end, ma non nel secondo esempio? – Akavall

+0

Penso che questa sia la risposta giusta, ma potrebbe essere meglio se qualche informazione fosse aggiunta: un modo (o modi) per ottenere il codice di esempio da aspettare ... – Digikata

+0

Aggiunta spiegazione e un esempio su come assicurarsi che il valore inviato su il canale viene stampato. – icza