Goroutine Leak, và debug resource: https://viblo.asia/p/golang-leaks-in-goroutine-and-debug-resource-3kY4g5q0LAe
prevent-leak
- Trong ví dụ trước đã nói về leak cũng như debug resource bằng
pprof
- Thì bài viết này hỗ trợ làm sao để không bị leak khi sử dụng
goroutine
tronggolang
Ví dụ 1;
- Để thành công trong việc giảm thiểu leak trong goroutine thì dùng
channel
giữa cácgoroutine cha và con
. Bởi theo quy định, signal luôn luôn chỉ đọc, vàgoroutine
cha passchannel
đến goroutine con. Khichannel
close, nó sẽ close cả goroutine con. - Code example:
func Preven() { doWork := func(done <-chan interface{}, strings <-chan string) <-chan interface{} { // (1) terminated := make(chan interface{}) go func() { defer fmt.Println("doWork exited.") defer close(terminated) for { select { case s := <-strings: // Do something interesting fmt.Println(s) case <-done: // (2) fmt.Println("done in work") return } } }() return terminated } done := make(chan interface{}) terminated := doWork(done, nil) go func() { // (3) // Cancel the operation after 1 second. time.Sleep(1 * time.Second) fmt.Println("Canceling doWork goroutine...") close(done) }() <-terminated // (4) fmt.Println("Done.")
}
- Ghi chú:
- (1)
doWork()
là một function bình thường, khai báo trong funcPrevent()
. Nhận vào 2 parameter và return 1 parameter - (2) Trong line này, dùng
for-select
pattern, trong case<-done
là kiểm trachannel
có được báo tín hiệu chưa, nếu có thì sẽ returngoroutine
- (3) Tạo một
goroutine
khác, mục đích để canceldoWork
sau thời gian 1 giây - (4) Để merge 2 goroutine lại với nhau, tiếp tục process những phần khác.
- (1)
- Kêt quả:
Canceling doWork goroutine...
done in work
doWork exited.
Done.
- Như kết quả, mặc dù trong function
doWork()
truyềnstring=nil
nhưng goroutine vẫn có thể exit, và clean-up resource. - Để có thể join 2
goroutine
lại với nhau, tạo thêm 1goroutine
thứ 3, mục đích đểcancel
funcdoWork()
sau 1 giây.
Ví dụ 2:
- Trong ví dụ này, thử nghiệm thêm trường hợp đó là nhận
value
từchannel
- Code example:
func LeakReceive() { newRandStream := func() <-chan int { randStream := make(chan int) go func() { defer fmt.Println("newRandStream closure exited.") // (1) defer close(randStream) for { randStream <- rand.Int() } }() return randStream } randStream := newRandStream() fmt.Println("3 random ints:") for i := 1; i <= 3; i++ { fmt.Printf("%d: %d\n", i, <-randStream) }
}
- Ghi chú:
- (1) khi dòng này xuất hiện thì
goroutine
đã được remove thành công.
- (1) khi dòng này xuất hiện thì
- Kêt quả:
3 random ints:
1: 5577006791947779410
2: 8674665223082153551
3: 6129484611666145821
-
Trong print out, không có hàm
defer fmt.Println
, điều này đồng nghĩa nó không được thực thi =>leak
. -
Sau khi 3 lần lặp,
goroutine
đã bị block và cố gắng sendrandom number
ra bên ngoài, nhưng có cóchannel
read. Có nghĩa không có cách nào đểstop goroutine
đang chạyrandom-number
-
Giải pháp, code:
func PreventLeakReceive() { newRandStream := func(done <-chan interface{}) <-chan int { randStream := make(chan int) go func() { defer fmt.Println("newRandStream closure exited.") defer close(randStream) for { select { case randStream <- rand.Int(): case <-done: return } } }() return randStream } done := make(chan interface{}) randStream := newRandStream(done) fmt.Println("3 random ints:") for i := 1; i <= 3; i++ { fmt.Printf("%d: %d\n", i, <-randStream) } close(done) // Simulate ongoing work time.Sleep(1 * time.Second)
}
- Ghi Chú:
- Như ví dụ trước, cũng tạo thêm 1
channle
, 1goroutine
thứ 3 =>terminates goroutine
thành công
- Như ví dụ trước, cũng tạo thêm 1
- Kết quả:
3 random ints:
1: 5577006791947779410
2: 8674665223082153551
3: 6129484611666145821
newRandStream closure exited.
- Như đã nhìn thấy, goroutine thực sữ đã được clean-up.