Vì cancel là thứ giải phóng tài nguyên gắn với context đó, kể cả khi công việc đã hoàn tất bình thường.
Mỗi context con được đăng ký vào danh sách con của context cha. Với WithTimeout / WithDeadline còn có thêm một time.Timer đang chạy. Nếu không gọi cancel:
- Context con nằm lại trong cha cho tới khi cha bị hủy — với cha là context.Background() thì nghĩa là suốt vòng đời tiến trình.
- Timer sống tới lúc hết hạn mới được thu hồi.
Gọi nhiều lần vô hại: cancel là idempotent. Nên defer cancel() ngay sau khi tạo là đúng trong mọi trường hợp.
ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
defer cancel()
rows, err := db.QueryContext(ctx, query, id)go vet có analyzer lostcancel bắt lỗi này: "the cancel function is not used on all paths (possible context leak)".
Đây là một trong những cảnh báo go vet không nên bỏ qua — chạy go vet ./... trong CI.