Select


🧠 1. select trong Go là gì?

Trong Go, select được dùng để chọn một case có thể thực thi từ nhiều channel. Nó hoạt động giống như switch, nhưng dành riêng cho channel operations (send/receive).

Cú pháp:

select {
case val := <-ch1:
    // nhận từ ch1
case ch2 <- val:
    // gửi vào ch2
default:
    // nếu không có case nào sẵn sàng
}

🚀 2. Cách select hoạt động (theo chiều sâu)

  • select sẽ chờ cho đến khi có ít nhất một case sẵn sàng (có thể gửi hoặc nhận).

  • Nếu nhiều case sẵn sàng cùng lúc → Go runtime sẽ chọn ngẫu nhiên một case.

  • Nếu không có case nào sẵn sàng và không có defaultselect block.

  • Nếu có default, select không block và thực hiện luôn default.


🔧 3. Ứng dụng thực tế

✅ Ví dụ 1: Timeout cho channel

select {
case msg := <-dataChan:
    fmt.Println("Received:", msg)
case <-time.After(2 * time.Second):
    fmt.Println("Timeout!")
}

👉 Đây là cách thường dùng để tránh deadlock khi chờ channel quá lâu.


✅ Ví dụ 2: Multiplexing nhiều channel

for {
    select {
    case msg1 := <-ch1:
        fmt.Println("From ch1:", msg1)
    case msg2 := <-ch2:
        fmt.Println("From ch2:", msg2)
    }
}

👉 Cách thường dùng để nghe nhiều nguồn dữ liệu bất đồng bộ cùng lúc.


⚠️ 4. Những lỗi phổ biến khi dùng select

❌ Không đóng channel → memory leak

go func() {
    for val := range ch {
        // logic xử lý
    }
}()

Nếu không close(ch) đúng lúc → goroutine này sẽ bị block mãi mãi trong range, gây leak.


❌ Không có default nhưng tất cả case đều block

select {
case <-ch: // ch không có dữ liệu
    // block mãi mãi
}

Giải pháp:

  • Thêm default

  • Đảm bảo ít nhất một case có thể sẵn sàng


🧩 5. Một số tips nâng cao

✴️ Dùng với context.Context để cancel goroutine

select {
case <-ctx.Done():
    fmt.Println("Context canceled")
case val := <-data:
    fmt.Println("Data:", val)
}

👉 Cách chuẩn để dọn dẹp goroutine khi cần cancel (ví dụ hết thời gian, client disconnect, etc.)


✴️ Loop với select và time.Ticker (polling)

ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()

for {
    select {
    case <-ticker.C:
        fmt.Println("Tick")
    case <-done:
        return
    }
}

👉 Rất hữu ích trong worker pool, background task, job scheduler...


🔚 Tổng kết

Ý tưởng
Tóm tắt

select

Dùng để xử lý nhiều channel cùng lúc

Non-blocking

Thêm default để tránh block

Timeout

Kết hợp với time.After

Cancel

Kết hợp với context.Context

Leak

Nhớ đóng channel hoặc handle cancel properly


Last updated