Câu lệnh select
Thuộc phần Lập trình hướng đối tượng trong hành trình GO của Coddy. Bài học 62 trên 107.
Câu lệnh select cho phép một goroutine chờ đồng thời nhiều thao tác trên kênh. Nó giống câu lệnh switch, nhưng mỗi nhánh liên quan đến việc gửi hoặc nhận qua một kênh. Khi nhiều kênh sẵn sàng, select chọn ngẫu nhiên một kênh.
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() { ch1 <- "from channel 1" }()
go func() { ch2 <- "from channel 2" }()
select {
case msg1 := <-ch1:
fmt.Println(msg1)
case msg2 := <-ch2:
fmt.Println(msg2)
}
}Nếu không có select, việc nhận dữ liệu từ ch1 sẽ bị chặn cho đến khi có dữ liệu, có thể khiến bạn bỏ lỡ dữ liệu trên ch2. Với select, kênh nào nhận dữ liệu trước sẽ được xử lý trước.
Trường hợp default giúp select không bị chặn. Nó thực thi ngay nếu không có kênh nào sẵn sàng:
select {
case msg := <-messages:
fmt.Println("Received:", msg)
default:
fmt.Println("No message available")
}Một mẫu phổ biến kết hợp select với thời gian chờ bằng time.After, hàm này trả về một kênh nhận giá trị sau khoảng thời gian được chỉ định:
select {
case result := <-ch:
fmt.Println("Got result:", result)
case <-time.After(2 * time.Second):
fmt.Println("Timeout!")
}Điều này giúp chương trình không phải chờ mãi khi một thao tác trên kênh có thể không bao giờ hoàn tất.
Thử thách
DễHãy xây dựng một trình giám sát dịch vụ sử dụng câu lệnh select để xử lý nhiều nguồn dữ liệu đồng thời. Trình giám sát sẽ lắng nghe các kênh dịch vụ khác nhau cùng lúc và phản hồi phù hợp khi có dữ liệu đến hoặc khi không có bản cập nhật nào.
Bạn sẽ sắp xếp code trong hai tệp:
monitor.go: Định nghĩa logic giám sát dịch vụ bằng cách xử lý các kênh dựa trên select.Tạo một struct
ServiceUpdatevới các trườngName(string) vàStatus(string).Triển khai hàm
Monitornhận ba kênh:primary chan ServiceUpdate,backup chan ServiceUpdatevàdone chan bool. Hàm này cần dùngselectđể lắng nghe đồng thời cả ba kênh:- Khi nhận dữ liệu từ
primary: trả về chuỗi đã định dạngPrimary: [Name] is [Status] - Khi nhận dữ liệu từ
backup: trả về chuỗi đã định dạngBackup: [Name] is [Status] - Khi nhận dữ liệu từ
done: trả vềMonitoring stopped
Triển khai hàm
CheckStatusnhận một kênh duy nhấtupdates chan ServiceUpdatevà dùngselectvới trường hợpdefaultđể kiểm tra không chặn:- Nếu có bản cập nhật: trả về
Update available: [Name] - Nếu không có bản cập nhật (trường hợp default): trả về
No updates pending
- Khi nhận dữ liệu từ
main.go: Thiết lập các kênh và minh họa cách hoạt động của select có chặn và không chặn.Đọc một chế độ từ đầu vào để xác định tình huống cần chạy:
primary,backup,stophoặccheck.Với các chế độ
primary,backupvàstop: đọc thêm tên và trạng thái dịch vụ. Tạo ba kênh, chạy một goroutine gửi dữ liệu vào kênh phù hợp dựa trên chế độ, sau đó gọiMonitorvà in kết quả.Với chế độ
check: đọc tên và trạng thái dịch vụ, cùng một cờ (sendhoặcnosend) cho biết có gửi dữ liệu trước khi kiểm tra hay không. Tạo một kênh có bộ đệm với sức chứa 1. Nếu cờ làsend, gửi một bản cập nhật vào kênh trước khi gọiCheckStatus. In kết quả củaCheckStatus.
Đầu vào sẽ có định dạng như sau:
- Dòng 1: Chế độ (
primary,backup,stophoặccheck) - Dòng 2: Tên dịch vụ
- Dòng 3: Trạng thái dịch vụ
- Dòng 4 (chỉ dành cho chế độ
check):sendhoặcnosend
Ví dụ, với đầu vào:
primary
Database
healthyKết quả đầu ra sẽ là:
Primary: Database is healthyVà với đầu vào:
check
Cache
active
nosendKết quả đầu ra sẽ là:
No updates pendingTự làm thử
package main
import (
"fmt"
)
func main() {
var mode string
fmt.Scanln(&mode)
var serviceName string
fmt.Scanln(&serviceName)
var serviceStatus string
fmt.Scanln(&serviceStatus)
// TODO: Xử lý các chế độ "primary", "backup", "stop", "check"
// Với các chế độ "primary", "backup", "stop":
// - Tạo ba kênh: primary, backup, done
// - Khởi chạy một goroutine để gửi dữ liệu đến kênh phù hợp
// - Gọi Monitor và in ra kết quả
// Với chế độ "check":
// - Đọc thêm cờ ("send" hoặc "nosend")
// - Tạo một kênh có bộ đệm với sức chứa 1
// - Nếu cờ là "send", gửi một bản cập nhật vào kênh
// - Gọi CheckStatus và in ra kết quả
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Nền tảng OOP trong Go
Tệp bên ngoàiWorkspace và module trong GoPackage và importTên được export và không được exportGiới thiệu về OOP trong GoStruct như lớpĐịnh nghĩa phương thức cho structReceiver kiểu con trỏ và kiểu giá trịKhởi tạo structHàm khởi tạoÔn tập: máy tính đơn giản4Interface
Giới thiệu về interfaceTriển khai ngầm địnhInterface như một hợp đồngInterface rỗng (any)Khẳng định kiểuswitch kiểuKết hợp interfaceInterface Stringer và ErrorÔn tập: Máy tính hình học7Tính đóng gói
Trường được xuất và không được xuấtĐóng gói ở cấp packagePhương thức getter và setterẨn thông tin trong GoÔn tập: hồ sơ học sinh10Generics (Go 1.18 trở lên)
Giới thiệu về GenericsTham số kiểuRàng buộc kiểuStruct tổng quátGiải pháp thay thế cho phương thức tổng quátÔn tập: tập hợp tổng quát13Các mẫu thiết kế phần 1
Giới thiệu về mẫu thiết kếMẫu SingletonMẫu FactoryMẫu Abstract FactoryMẫu ObserverMẫu Strategy2Tìm hiểu chuyên sâu về kiểu dữ liệu và struct
Kiểu cơ bản và kiểu tổng hợpĐịnh nghĩa kiểu dữ liệu tùy chỉnhThẻ structStruct ẩn danhStruct lồng nhauGiá trị zero và giá trị mặc địnhÔn tập: sổ liên lạc5Ưu tiên kết hợp hơn kế thừa
Vì sao Go không có kế thừaNhập môn nhúng structTự động nâng phương thứcNhúng nhiều structNhúng và kết tậpChe khuất phương thức được nhúngÔn tập: hệ phân cấp nhân viên8Xử lý lỗi và lập trình hướng đối tượng
Giao diện errorCác kiểu lỗi tùy chỉnhBọc lỗi với fmt.ErrorfLỗi sentinelerrors.Is() và errors.As()Panic, defer và recoverÔn tập: trình phân tích tệp3Con trỏ và bộ nhớ
Cơ bản về con trỏ trong GoCon trỏ đến structTruyền theo giá trị và tham chiếuHàm new()Thu gom rác trong GoÔn tập: xây dựng danh sách liên kết6Đa hình trong Go
Đa hình qua interfaceĐịnh kiểu vịt trong GoQuy tắc thỏa mãn interfaceCác tập hợp đa hìnhTiêm phụ thuộcÔn tập: bộ xử lý thanh toán9Lập trình đồng thời và lập trình hướng đối tượng
Cơ bản về goroutineChannel và giao tiếpChannel có bộ đệm và không có bộ đệmCâu lệnh selectsync.Mutex và sync.RWMutexsync.WaitGroupThiết kế struct an toàn luồngÔn tập: worker poolTự luyện tập: Trình biên dịch Go online