Bài học theo mô-đun (3/5)
`select`: ghép kênh
select chờ hoạt động trên nhiều kênh cùng một lúc.
Đó là switch của tính đồng thời của cờ vây: nó cho phép bạn phản ứng với lần đầu tiên
kênh đã sẵn sàng trong số nhiều kênh.
Cú pháp
select {
case v := <-ch1:
fmt.Println("ch1:", v)
case v := <-ch2:
fmt.Println("ch2:", v)
case ch3 <- 99:
fmt.Println("inviato su ch3")
case <-time.After(time.Second):
fmt.Println("timeout")
default:
fmt.Println("nessuno è pronto")
}Ngữ nghĩa:
- Tất cả các trường hợp đều được đánh giá; chỉ một trong số các trường hợp sẵn sàng được chọn.
- Nếu nhiều trường hợp đã sẵn sàng, lựa chọn là giả ngẫu nhiên (không có thứ tự).
- Nếu chưa có trường hợp nào sẵn sàng: chặn
selectcho đến khi một trường hợp sẵn sàng. - Nếu có
default: nó sẽ chạy ngay lập tức khi không có trường hợp nào sẵn sàng (không chặn chọn).
Mẫu thời gian chờ
select {
case res := <-fetch(url):
use(res)
case <-time.After(2 * time.Second):
log.Println("timeout: fetch troppo lento")
}time.After(d) trả về <-chan Time tạo ra giá trị sau d.
Đó là cách cổ điển để hạn chế sự chờ đợi.
Chọn không chặn theo mặc định
select {
case msg := <-ch:
process(msg)
default:
// niente da fare, prosegui
}Nó "thăm dò" kênh mà không chặn. Hữu ích trong các đường dẫn mã phải luôn đáp ứng mà không bắt buộc phải chờ đợi.
Mẫu "Kênh hoàn tất"
Kết hợp select với kênh hủy:
func worker(in <-chan Job, done <-chan struct{}) {
for {
select {
case j := <-in:
process(j)
case <-done:
return // chi gestisce done segnala lo shutdown
}
}
}close(done) từ điều phối viên sẽ kích hoạt case <-done trong mọi
goroutine chờ: phối hợp tắt máy.
Lặp lại với lựa chọn
for {
select {
case v := <-input:
if v == nil {
return
}
handle(v)
case <-time.After(5 * time.Second):
keepAlive()
case <-ctx.Done():
return
}
}Mẫu "vòng lặp sự kiện" điển hình dành cho máy chủ và nhân viên.
Kênh không là trường hợp "bị vô hiệu hóa"
Thủ thuật nâng cao: case <-ch với ch == nil là không bao giờ
đã chọn. Nó cho phép bạn "vô hiệu hóa" một trường hợp một cách linh hoạt:
var in chan int = source()
for {
select {
case v, ok := <-in:
if !ok {
in = nil // disabilita questo case
continue
}
handle(v)
case <-ctx.Done():
return
}
}Khi in bị đóng, tôi vô hiệu hóa nó: select chỉ tiếp tục xử lý
các trường hợp khác.
Hãy thử nó
Thực hiện lựa chọn với hai trường hợp nhận từ ch1 và ch2; in cái nào đến trước.
Hiển thị gợi ý
Cú pháp `select { case ...: ... }` với một trường hợp trên mỗi kênh.
Giải pháp khả dụng sau 3 lần thử
Thêm trường hợp hết thời gian chờ vào lựa chọn bằng cách sử dụng time.After(time.Second).
Hiển thị gợi ý
`time.After(d)` là kênh nhận sau d.
Giải pháp khả dụng sau 3 lần thử
Điều gì làm cho việc chọn không bị chặn?
select { case v := <-ch: ... ??? }Tóm tắt lại
selectchờ trường hợp sẵn sàng đầu tiên trong số một số hoạt động kênh.- Lựa chọn giả ngẫu nhiên trong số nhiều trường hợp sẵn sàng.
default→ chọn không chặn (bỏ phiếu).time.After(d)khi hết thời gian chờ (lưu ý đến rò rỉ trong các vòng lặp: sử dụngcontext).- Các mẫu: kênh xong, vòng lặp sự kiện, kênh nil để vô hiệu hóa một trường hợp.
- Trống
select{}= khối vĩnh viễn: hầu như luôn là lỗi.