Bài học theo mô-đun (5/5)
`context.Context`: hủy bỏ và thời hạn
Gói context là tiêu chuẩn thực tế để truyền bá
hủy, thời hạn và giá trị giới hạn phạm vi dọc theo chuỗi cuộc gọi,
trên nhiều goroutines. Tất cả các gói "kết nối mạng" tiêu chuẩn
(net/http, database/sql, os/exec) chấp nhận context.Context làm
tham số đầu tiên của họ.
Giao diện
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key any) any
}Done(): kênh bị đóng khi ngữ cảnh bị hủy.Err(): lý do hủy (context.Canceledhoặccontext.DeadlineExceeded).Deadline(): thời hạn tùy chọn.Value(): tra cứu các giá trị giới hạn phạm vi (sử dụng tiết kiệm).
Bốn chức năng cơ bản
ctx := context.Background() // radice: usalo in main, init, test
ctx := context.TODO() // "non so ancora": stub esplicito
ctx, cancel := context.WithCancel(parent) // cancel manuale
ctx, cancel := context.WithTimeout(parent, d) // scadenza relativa
ctx, cancel := context.WithDeadline(parent, t) // scadenza assoluta
ctx := context.WithValue(parent, key, value) // valore scope-boundTất cả các dạng dẫn xuất đều trả về hàm cancel: luôn gọi nó
(lý tưởng nhất là với defer) để giải phóng tài nguyên nội bộ, ngay cả khi
context đã hết hạn.
Mẫu thời gian chờ
func fetchUser(id string) (*User, error) {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
return queryUser(ctx, id) // queryUser deve rispettare ctx
}Nếu queryUser mất hơn 2 giây, ctx.Done() sẽ bị đóng và
chức năng phải hủy bỏ hoạt động.
Tôn trọng bối cảnh: chọn Done()
func slowWork(ctx context.Context) error {
for {
select {
case <-ctx.Done():
return ctx.Err() // canceled o deadline exceeded
case <-time.After(100 * time.Millisecond):
// un tick di lavoro
}
}
}Quy tắc: bất cứ nơi nào chức năng của bạn có thể bị chặn trong thời gian dài, nó sẽ phải xử lý ctx.Done(). Nếu không thì bối cảnh sẽ được "bật nguồn" cho không có gì.
Quy ước tham số đầu tiên
func Fetch(ctx context.Context, url string) ([]byte, error) { ... }
// ^^^^^^^^^^^^^^^^^^^
// SEMPRE primo, mai dentro a una struct, mai opzionalectxLUÔN LUÔN làm tham số đầu tiên, được đặt tên làctx.- Không bao giờ đặt
Contextlàm trường của cấu trúc (rất hiếm trường hợp ngoại lệ). - Không bao giờ vượt qua
nil: sử dụngcontext.TODO()làm sơ khai.
bối cảnh.WithValue: một cách tiết kiệm
type userKey struct{}
ctx := context.WithValue(ctx, userKey{}, currentUser)
// più sotto:
if u, ok := ctx.Value(userKey{}).(*User); ok { ... }Hướng dẫn:
- CHỈ dành cho dữ liệu trong phạm vi yêu cầu (ID yêu cầu, ID theo dõi, danh tính người dùng).
- KHÔNG BAO GIỜ cho các tham số hàm: truyền chúng một cách bình thường.
- Khóa thuộc loại tùy chỉnh không xuất để tránh xung đột.
Tuyên truyền dọc theo chuỗi
func handleHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() // context legato al ciclo di vita della richiesta
user, err := fetchUser(ctx, r.URL.Query().Get("id"))
if err != nil { ... }
// ...
}net/http tự động hủy r.Context() khi khách hàng
ngắt kết nối hoặc khi trình xử lý quay trở lại. Bằng cách truyền bá nó đến tất cả mọi người
các cuộc gọi xuôi dòng, toàn bộ cây hoạt động bị hủy bỏ trong
tầng: không lãng phí công việc sau khi ngắt kết nối.
Hãy thử nó
Tạo bối cảnh có thời gian chờ 1 giây bắt nguồn từ context.Background() và nhớ gọi hủy bằng defer.
Hiển thị gợi ý
`context.WithTimeout(parent, d)` trả về `(ctx, cancel)`; hủy cuộc gọi luôn.
Giải pháp khả dụng sau 3 lần thử
Sử dụng <-ctx.Done() trong phần chọn để chờ ngữ cảnh bị hủy.
Hiển thị gợi ý
`ctx.Done()` là một kênh đóng khi ngữ cảnh bị hủy.
Giải pháp khả dụng sau 3 lần thử
Theo quy ước, tham số ctx nằm ở đâu trong chữ ký hàm?
func Fetch(???, url string) ([]byte, error)Tóm tắt lại
context.Contexttruyền bá các giá trị hủy, thời hạn và giới hạn phạm vi.Background()/TODO()làm gốc;WithCancel/WithTimeout/WithDeadlineđể rút ra.- Luôn
defer cancel()để giải phóng tài nguyên. - Các hàm "Dài" phải
selecttrênctx.Done()hoặc chuyển ctx sang các gói tiêu chuẩn. - Quy ước:
ctxlàm tham số FIRST, không bao giờ nằm trong cấu trúc. WithValuechỉ dành cho dữ liệu trong phạm vi yêu cầu (ID yêu cầu, ID theo dõi).- Trong
net/http,r.Context()bị hủy khi ngắt kết nối máy khách.