2013-05-16 9 views
9

Si consideri il seguente codice come un esempio semplificato:Il garbage collector raccoglierà le routine Go che non continueranno mai?

func printer(c <-chan int) { 
    for { 
     fmt.Print(<-c) 
    } 
} 

func provide() { 
    c := make(chan int) 

    go printer(c) 

    for i := 1; i <= 100; i++ { 
     c <- i 
    } 
} 

La funzione provide crea un andare di routine printer che stampa i dati provide genera.

La mia domanda è, cosa succede dopo i ritorni provide e printer inizia il blocco sul canale vuoto. La routine di routine andrà persa, poiché non vi sono ulteriori riferimenti a c o il garbage collector prenderà questo caso e disporrà sia la routine go che c?

Se è davvero il caso che questo tipo di codice causi una perdita di memoria, quali strategie posso fare per evitare che si verifichi una tale perdita di memoria?

risposta

10

Chiudere il canale. La lettura da un canale chiuso ha sempre successo, restituendo un rispettivo valore zero. Il secondo valore restituito booleano opzionale indica la validità del primo valore.

Receive operator:

Un ricevono espressione usata in un'assegnazione o inizializzazione del modulo

x, ok = <-ch 
x, ok := <-ch 
var x, ok = <-ch 

cede un ulteriore risultato di tipo bool segnalato se la comunicazione è riuscita. Il valore di ok è true se il valore ricevuto è stato recapitato da un'operazione di invio andata a buon fine nel canale o false se è un valore zero generato perché il canale è chiuso e vuoto.

func printer(c <-chan int) { 
     for { 
       v, ok := <-c 
       if !ok { // chan closed 
         return 
       } 

       // v is valid 
       fmt.Println(v) 
     } 
} 

func provide() { 
     c := make(chan int) 

     go printer(c) 

     for i := 1; i <= 100; i++ { 
       c <- i 
     } 
     close(c) 
} 
+1

Grazie. Apparentemente la mia "soluzione" non ha funzionato bene. – fuz

+3

utilizzando una clausola di intervallo nella goroutine della stampante eviterà il controllo ok -> per v: = intervallo c {fmt.Println (v)} – Philipp

+0

@Philipp: Sì, hai ragione. – zzzz

0

Provare il seguente programma per verificare che questo effettivamente perdite di memoria. Si noti che questo programma consuma la RAM piuttosto rapidamente; essere pronti a ucciderlo.

package main 

func worker(c <-chan int) { 
    var i int 

    for { 
     i += <-c 
    } 
} 

func wrapper() { 
    c := make(chan int) 

    go worker(c) 

    for i := 0; i < 0xff; i++ { 
     c <- i 
    } 
} 

func main() { 
    for { 
     wrapper() 
    } 
} 

Per risolvere la perdita, chiudere un canale a cui fa riferimento la routine go orfana. Il runtime nota che una routine di Go che legge solo da canali chiusi non continuerà mai e procede a liberarla. Il codice fisso è simile al seguente:

package main 

func worker(c <-chan int) { 
    var i int 

    for { 
     i += <-c 
    } 
} 

func wrapper() { 
    c := make(chan int) 
    defer close(c) // fix here 

    go worker(c) 

    for i := 0; i < 0xff; i++ { 
     c <- i 
    } 
} 

func main() { 
    for { 
     wrapper() 
    } 
}