Menu
CoddyTech

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.

challenge icon

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 ServiceUpdate với các trường Name (string) và Status (string).

    Triển khai hàm Monitor nhận ba kênh: primary chan ServiceUpdate, backup chan ServiceUpdate và done chan bool. Hàm này cần dùng select để lắng nghe đồng thời cả ba kênh:

    • Khi nhận dữ liệu từ primary: trả về chuỗi đã định dạng Primary: [Name] is [Status]
    • Khi nhận dữ liệu từ backup: trả về chuỗi đã định dạng Backup: [Name] is [Status]
    • Khi nhận dữ liệu từ done: trả về Monitoring stopped

    Triển khai hàm CheckStatus nhận một kênh duy nhất updates chan ServiceUpdate và dùng select với trường hợp default để 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
  • 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, stop hoặc check.

    Với các chế độ primary, backup và 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ọi Monitor và in kết quả.

    Với chế độ check: đọc tên và trạng thái dịch vụ, cùng một cờ (send hoặc nosend) 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ọi CheckStatus. In kết quả của CheckStatus.

Đầu vào sẽ có định dạng như sau:

  • Dòng 1: Chế độ (primary, backup, stop hoặc check)
  • 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): send hoặc nosend

Ví dụ, với đầu vào:

primary
Database
healthy

Kết quả đầu ra sẽ là:

Primary: Database is healthy

Và với đầu vào:

check
Cache
active
nosend

Kết quả đầu ra sẽ là:

No updates pending

Tự 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ả
	
}
quiz iconTự kiểm tra

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

Tự luyện tập: Trình biên dịch Go online