Kết quả sau khoảng 2 giây: Goroutine doWork sẽ dừng khi context timeout kết thúc, trả về lý do context deadline exceeded
.
context.Background()
hoặc context.TODO()
, sau đó ta có thể tạo context con với đặc điểm riêng biệt.Loại Context | Mục Đích | Ví Dụ Hàm |
---|---|---|
Context với hủy thủ công | Cho phép hủy bỏ thủ công khi cần | context.WithCancel |
Context với timeout | Tự động hủy sau một khoảng thời gian định trước | context.WithTimeout |
Context với deadline | Tự động hủy khi đến thời điểm nhất định | context.WithDeadline |
ctx, cancel := context.WithCancel(context.Background())go doSomething(ctx)time.Sleep(1 * time.Second)cancel()
cancel()
sau khi tạo context có hủy bỏ để tránh rò rỉ tài nguyên.context.Background()
khi không có context cụ thể.func fetchUserData(ctx context.Context, userID string) (string, error) { select { case <-time.After(2 * time.Second): return "User data", nil case <-ctx.Done(): return "", ctx.Err() }}
func processRequest(ctx context.Context, userID string) (string, error) { data, err := fetchUserData(ctx, userID) if err != nil { return "", err } return "Processed: " + data, nil}
Nếu ctx
hết hạn trước khi fetchUserData xong, lỗi timeout sẽ được đẩy lên xử lý kịp thời.
context.WithValue
, giúp truyền dữ liệu tạm thời theo request như ID người dùng hoặc token.type contextKey stringconst userIDKey contextKey = "userID"
ctx := context.WithValue(context.Background(), userIDKey, "user123")
func process(ctx context.Context) { userID, ok := ctx.Value(userIDKey).(string) if !ok { fmt.Println("No user ID") return } fmt.Println("Processing for user:", userID)}
Sai Lầm | Hậu Quả | Cách Khắc Phục |
---|---|---|
Quên gọi cancel() | Rò rỉ tài nguyên goroutine | Luôn dùng defer cancel() |
Truyền nil thay vì context | Lỗi runtime hoặc không kiểm soát | Dùng context.Background() |
Lạm dụng context.WithValue | Làm phức tạp và khó bảo trì | Truyền tham số qua hàm thường xuyên |
Ví dụ: Thiếu defer cancel():
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)// forgot defer cancel()go worker(ctx)
func handler(w http.ResponseWriter, r *http.Request) { ctx := r.Context() // context đi kèm HTTP request select { case <-time.After(2 * time.Second): fmt.Fprint(w, "Done!") case <-ctx.Done(): fmt.Println("Client disconnected, reason:", ctx.Err()) }}
Khi người dùng hủy request (như đóng trình duyệt), server kịp thời xử lý ngắt tác vụ đang chạy.
context.Background()
.defer cancel()
sau khi tạo context có khả năng hủy bỏ.context.WithTimeout
hoặc WithDeadline
cho các tác vụ cần giới hạn thời gian.context.WithValue
ngoài mục đích request-scoped.ctx.Err()
sau khi nhận tín hiệu hủy.Hãy thử áp dụng ngay context trong dự án Go tiếp theo của bạn để cảm nhận sự khác biệt!