Go語言使用select監(jiān)聽多個(gè)channel的示例詳解
一、前言:為什么要使用select
在 Go 的并發(fā)編程中,channel 是協(xié)程間通信的核心工具。但當(dāng)你需要同時(shí)從多個(gè) Channel 中接收數(shù)據(jù),或者在特定時(shí)間內(nèi)做出響應(yīng),select 就派上了大用場。
它類似于網(wǎng)絡(luò)編程中的 select/poll/epoll,用于監(jiān)聽多個(gè) IO 操作的就緒狀態(tài),只不過這里的 IO 是 Channel 的讀寫操作。
二、實(shí)戰(zhàn)目標(biāo)
我們將構(gòu)建一個(gè)并發(fā)任務(wù)監(jiān)聽系統(tǒng),實(shí)現(xiàn)以下目標(biāo):
- 啟動(dòng)多個(gè)任務(wù)(Goroutine),分別向不同的 Channel 寫入結(jié)果
- 使用
select監(jiān)聽多個(gè) Channel,響應(yīng)哪個(gè)就處理哪個(gè) - 實(shí)現(xiàn)超時(shí)處理
- 實(shí)現(xiàn) Channel 關(guān)閉退出機(jī)制
三、案例代碼:監(jiān)聽兩個(gè)任務(wù)結(jié)果和超時(shí)
package main
import (
"fmt"
"math/rand"
"time"
)
func task(name string, delay time.Duration, ch chan<- string) {
time.Sleep(delay)
ch <- fmt.Sprintf("任務(wù) %s 完成(耗時(shí) %v)", name, delay)
}
func main() {
rand.Seed(time.Now().UnixNano())
task1Ch := make(chan string)
task2Ch := make(chan string)
// 啟動(dòng)兩個(gè)任務(wù)
go task("A", time.Duration(rand.Intn(2000))*time.Millisecond, task1Ch)
go task("B", time.Duration(rand.Intn(2000))*time.Millisecond, task2Ch)
// 使用 select 監(jiān)聽兩個(gè)任務(wù)完成情況
timeout := time.After(2 * time.Second) // 超時(shí)設(shè)置
for i := 0; i < 2; i++ {
select {
case res := <-task1Ch:
fmt.Println("[監(jiān)聽] 收到任務(wù)1結(jié)果:", res)
case res := <-task2Ch:
fmt.Println("[監(jiān)聽] 收到任務(wù)2結(jié)果:", res)
case <-timeout:
fmt.Println("[監(jiān)聽] 等待超時(shí)!")
return
}
}
fmt.Println("主程序退出。")
}
四、運(yùn)行示例
一次運(yùn)行輸出:
[監(jiān)聽] 收到任務(wù)2結(jié)果: 任務(wù) B 完成(耗時(shí) 1.379s)
[監(jiān)聽] 收到任務(wù)1結(jié)果: 任務(wù) A 完成(耗時(shí) 1.962s)
主程序退出。
另一次運(yùn)行(任務(wù)沒完成):
[監(jiān)聽] 等待超時(shí)!
五、重點(diǎn)解析:Go 中的select
1.select的行為特性
會(huì)隨機(jī)選擇一個(gè)可用的 case 執(zhí)行(如果多個(gè)都可讀)
如果沒有任何 case 準(zhǔn)備好,則阻塞,除非有 default
可以結(jié)合 time.After 實(shí)現(xiàn)超時(shí)控制
2. 實(shí)現(xiàn)超時(shí)機(jī)制
timeout := time.After(2 * time.Second)
這是 Go 提供的標(biāo)準(zhǔn)庫能力,會(huì)在 2 秒后向 timeout 這個(gè)只讀通道發(fā)送一個(gè)時(shí)間值,結(jié)合 select 使用非常方便。
3. 非阻塞通道監(jiān)聽(可選)
select {
case msg := <-ch:
fmt.Println("收到消息:", msg)
default:
fmt.Println("沒有消息,非阻塞處理")
}
六、延伸應(yīng)用場景
select 與 Channel 聯(lián)合使用,可以應(yīng)用于非常多的高并發(fā)場景:
| 場景 | 用法說明 |
|---|---|
| 多任務(wù)結(jié)果優(yōu)先處理 | 誰先完成就處理誰 |
| 多個(gè)服務(wù)節(jié)點(diǎn)返回最快結(jié)果 | 服務(wù)冗余設(shè)計(jì)中的快速返回策略 |
| 實(shí)現(xiàn)“心跳檢測”或“任務(wù)超時(shí)” | 每個(gè)協(xié)程都有監(jiān)聽超時(shí) Channel,任務(wù)不響應(yīng)就退出 |
| 限制任務(wù)處理時(shí)間(防止卡死) | 結(jié)合 context.WithTimeout() 更可靠 |
七、完整示例:結(jié)合for-select實(shí)現(xiàn)循環(huán)監(jiān)聽
func main() {
dataCh := make(chan string)
quitCh := make(chan bool)
// 模擬異步數(shù)據(jù)流
go func() {
for i := 0; i < 5; i++ {
dataCh <- fmt.Sprintf("數(shù)據(jù)包 #%d", i)
time.Sleep(time.Millisecond * 500)
}
quitCh <- true
}()
for {
select {
case msg := <-dataCh:
fmt.Println("接收數(shù)據(jù):", msg)
case <-quitCh:
fmt.Println("任務(wù)結(jié)束,退出監(jiān)聽循環(huán)")
return
case <-time.After(1 * time.Second):
fmt.Println("1秒無數(shù)據(jù),超時(shí)處理...")
}
}
}
八、總結(jié)
通過本案例你學(xué)會(huì)了:
- 如何使用
select同時(shí)監(jiān)聽多個(gè) Channel - 如何實(shí)現(xiàn)超時(shí)機(jī)制(
time.After) - 如何在主線程中等待異步任務(wù)完成
- 如何構(gòu)建可擴(kuò)展的并發(fā)通信模型
select 是 Go 并發(fā)編程的核心工具之一,它讓我們能夠優(yōu)雅地處理多路異步事件,是構(gòu)建高性能系統(tǒng)的利器。
到此這篇關(guān)于Go語言使用select監(jiān)聽多個(gè)channel的示例詳解的文章就介紹到這了,更多相關(guān)Go select監(jiān)聽hannel內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言Http?Server框架實(shí)現(xiàn)一個(gè)簡單的httpServer
這篇文章主要為大家介紹了Go語言Http?Server框架實(shí)現(xiàn)一個(gè)簡單的httpServer抽象,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
使用Golang的channel交叉打印兩個(gè)數(shù)組的操作
這篇文章主要介紹了使用Golang的channel交叉打印兩個(gè)數(shù)組的操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-04-04
golang gopm get -g -v 無法獲取第三方庫的解決方案
這篇文章主要介紹了golang gopm get -g -v 無法獲取第三方庫的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-05-05
go不同業(yè)務(wù)環(huán)境變量的設(shè)置方式
這篇文章主要介紹了go不同業(yè)務(wù)環(huán)境變量的設(shè)置方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-07-07

