2013-05-16 33 views
9

Xét đoạn mã sau như một ví dụ đơn giản:Bộ thu gom rác sẽ thu thập các thói quen Go sẽ không bao giờ tiếp tục?

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

Chức năng provide tạo ra một thói quen đi printer đó in các dữ liệu provide tạo ra.

Câu hỏi của tôi là, điều gì xảy ra sau khi provide trả về và printer bắt đầu chặn trên kênh trống. Việc rò rỉ thông thường có bị rò rỉ, vì không có tham chiếu thêm nào tới c hoặc bộ thu gom rác sẽ bắt được trường hợp này và vứt bỏ cả thói quen đi và c?

Nếu thực sự là trường hợp loại mã này gây ra rò rỉ bộ nhớ, tôi có thể làm những chiến lược nào để ngăn chặn sự rò rỉ bộ nhớ đó xảy ra?

Trả lời

10

Đóng kênh. Việc đọc từ kênh kín luôn thành công, trả lại giá trị bằng 0 tương ứng. Giá trị trả về boolean thứ hai tùy chọn cho biết giá trị của giá trị đầu tiên.

Receive operator:

Một nhận biểu hiện sử dụng trong một chuyển nhượng hoặc khởi tạo có dạng

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

mang lại thêm một kết quả của loại bool báo cáo xem thông tin liên lạc thành công. Giá trị của ok là true nếu giá trị nhận được được phân phối bởi một hoạt động gửi thành công tới kênh hoặc giả mạo nếu nó là giá trị bằng 0 do kênh bị đóng và trống.

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

Cảm ơn bạn. Rõ ràng "giải pháp" của tôi không thực sự hoạt động tốt. – fuz

+3

sử dụng mệnh đề phạm vi trong goroutine của máy in sẽ tránh kiểm tra ok -> cho v: = phạm vi c {fmt.Println (v)} – Philipp

+0

@Philipp: Đúng, bạn nói đúng. – zzzz

0

Hãy thử chương trình sau để xác minh rằng điều này thực sự làm rò rỉ bộ nhớ. Xin lưu ý rằng chương trình này ăn RAM của bạn khá nhanh; được chuẩn bị để giết nó.

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

Để giải quyết sự cố, hãy đóng kênh được tham chiếu bởi thói quen đi làm mồ côi hiện tại. Thời gian chạy thông báo rằng việc đọc thường trình Go từ các kênh đã đóng sẽ không bao giờ tiếp tục và tiếp tục giải phóng nó. Mã cố định trông giống như sau:

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