2013-05-16 96 views
9

考慮下面的代碼作爲一個簡化的例子:垃圾收集器會收集永不會繼續的Go例程嗎?

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

功能provide創建一個去例程printer,打印provide生成數據。

我的問題是,在provide返回和printer開始阻塞空信道後會發生什麼。去日常事務是否會泄漏,因爲沒有進一步提及c或者垃圾回收器是否會捕獲這種情況並處理去程序和c

如果確實是這種代碼導致內存泄漏的情況,我可以採取什麼策略來防止發生內存泄漏?

回答

10

關閉通道。從關閉的通道讀取總是成功的,返回相應的零值。可選的第二個布爾值返回值表示第一個值的有效性。

Receive operator

甲接收形式

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

產生bool類型報告的通信是否成功的附加結果的分配或初始化中使用的表達。如果收到的值是通過成功的發送操作傳遞到通道,則值爲ok;如果通道是關閉且爲空,則生成零值,則返回false。

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

謝謝。顯然,我的「解決方案」並不是很好。 – fuz

+3

在打印機goroutine中使用range子句將避免ok檢查 - >對於v:= range c {fmt.Println(v)} – Philipp

+0

@Philipp:是的,你說得對。 – zzzz

0

請嘗試以下程序以驗證這確實會泄漏內存。請注意,這個程序很快耗盡你的RAM;準備殺死它。

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

爲了解決泄漏問題,請關閉由現在孤立的去程序引用的通道。運行時注意到只有封閉通道的Go例程永遠不會繼續,並繼續釋放它。固定代碼如下所示:

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