Goroutine leak là goroutine được tạo ra nhưng không bao giờ kết thúc — nó chặn vĩnh viễn trên một thao tác không bao giờ hoàn tất. Goroutine đó giữ luôn stack, biến nó tham chiếu và cả connection nó đang cầm, nên GC không thu hồi được gì.
Bốn dạng phổ biến:
1. Gửi mà không còn ai nhận — dạng hay gặp nhất. Hàm cha return sớm (timeout, lỗi validate) nên không đọc kết quả, goroutine con chặn mãi ở lệnh gửi.
2. Nhận từ channel không ai đóng — worker range trên channel mà producer thoát sớm và quên close.
3. Không nghe tín hiệu huỷ — vòng lặp không có nhánh <-ctx.Done(), nên huỷ request cũng không dừng được nó.
4. Chặn trên lock / WaitGroup — wg.Wait() không bao giờ về vì thiếu một lần Done.
func fetch(ctx context.Context, url string) (string, error) {
ch := make(chan string) // unbuffered: bên gửi cần có bên nhận
go func() { ch <- httpGet(url) }()
select {
case v := <-ch:
return v, nil
case <-ctx.Done():
return "", ctx.Err() // không ai đọc ch nữa: goroutine bị leak
}
}Sửa dạng này chỉ cần make(chan string, 1) — bên gửi luôn đặt được giá trị rồi thoát, dù không ai đọc.
Nguyên tắc chung: không tạo goroutine nếu chưa biết nó sẽ dừng bằng cách nào.