2013-05-16 4 views
9

Rozważmy następujący kod jako uproszczonego przykładu:Czy odśmiecacz będzie zbierać procedury Go, które nigdy nie będą kontynuowane?

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

Funkcja provide tworzy przejść rutynowe printer która drukuje dane provide generuje.

Moje pytanie brzmi, co stanie się po powrocie do provide i rozpocznie blokowanie na pustym kanale. Czy rutynowa procedura zostanie przerwana, ponieważ nie ma już odniesienia do c lub czy moduł do zbierania śmieci wyłapie tę sprawę i wyrzuci zarówno procedurę rutyny, jak i c?

Jeśli rzeczywiście jest tak, że ten rodzaj kodu powoduje wyciek pamięci, jakie strategie mogę zrobić, aby zapobiec takiemu wyciekowi pamięci?

Odpowiedz

10

Zamknij kanał. Czytanie z zamkniętego kanału zawsze kończy się sukcesem, zwracając odpowiednią wartość zerową. Opcjonalna druga zwracana wartość boolowska wskazuje ważność pierwszej wartości.

Receive operator:

odbieranych wyrażenia stosowane w przypisania lub inicjowania postaci

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

Plony dodatkowy efekt typu bool raportowania czy komunikacja udało. Wartość ok jest prawdziwa, jeśli otrzymana wartość została dostarczona przez udaną operację wysyłania do kanału, lub wartość false, jeśli jest to wartość zero, ponieważ kanał jest zamknięty i pusty.

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

Dziękuję. Najwyraźniej moje "rozwiązanie" tak naprawdę nie działa dobrze. – fuz

+3

przy użyciu klauzuli zakresu w drukarce goroutine uniknie się sprawdzenia ok -> dla v: = zakresu c {fmt.Println (v)} – Philipp

+0

@Philipp: Tak, masz rację. – zzzz

0

Wypróbuj następujący program, aby sprawdzić, czy rzeczywiście wycieka z niego pamięć. Proszę zauważyć, że ten program dość szybko zżera pamięć RAM; bądź przygotowany, aby go zabić.

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

Aby rozwiązać problem nieszczelności, zamknij kanał, do którego odwołuje się teraz procedura osierocenia. Środowisko wykonawcze zauważa, że ​​rutynowe czytanie tylko z zamkniętych kanałów nie będzie kontynuowane i będzie kontynuowane, aby je zwolnić. Poprawiony kod wygląda następująco:

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() 
    } 
} 
Powiązane problemy