Go語言中select使用詳解
什么是 select?
select 是 Go 中用于 多路 channel 操作 的控制結(jié)構(gòu),它可以監(jiān)聽多個 channel 的發(fā)送與接收操作,當其中某一個可以進行時就執(zhí)行對應的語句,從而實現(xiàn)非阻塞并發(fā)通信。
基本語法
select {
case val := <-ch1:
// ch1 可讀時執(zhí)行
case ch2 <- 100:
// ch2 可寫時執(zhí)行
default:
// 所有 channel 都阻塞時執(zhí)行(可選)
}每個
case必須是 發(fā)送(ch <- val)或接收(val := <-ch)只會執(zhí)行一個可操作的
case(如果多個都可以隨機挑一個)如果都阻塞,且沒有
default,select會阻塞等待如果包含
default,它會立即執(zhí)行,哪怕其他case可能之后才可操作
使用場景舉例
1. 監(jiān)聽多個 channel 的數(shù)據(jù)
select {
case msg1 := <-ch1:
fmt.Println("收到 ch1:", msg1)
case msg2 := <-ch2:
fmt.Println("收到 ch2:", msg2)
}2. 實現(xiàn)超時機制
select {
case msg := <-ch:
fmt.Println("收到:", msg)
case <-time.After(2 * time.Second):
fmt.Println("超時")
}
time.After返回一個 channel,在指定時間后變?yōu)榭勺x,實現(xiàn)優(yōu)雅的 timeout。
3. 非阻塞發(fā)送或接收(default 分支)
select {
case ch <- data:
fmt.Println("發(fā)送成功")
default:
fmt.Println("channel 滿,放棄發(fā)送")
}4. 檢測通道是否關(guān)閉
select {
case v, ok := <-ch:
if !ok {
fmt.Println("通道關(guān)閉")
} else {
fmt.Println("收到:", v)
}
}select 的行為特點
| 行為 | 描述 |
|---|---|
| 隨機調(diào)度 | 多個 case 同時可用時隨機選擇一個執(zhí)行(防止饑餓) |
| 阻塞等待 | 所有 case 阻塞時,select 自身也阻塞 |
| default 分支 | 所有 case 阻塞時立即執(zhí)行,避免阻塞 |
| 只選一個 | 同時滿足多個時只執(zhí)行其中一個 |
與 goroutine 配合:生產(chǎn)者/消費者模型
func producer(ch chan int) {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
}
?
func consumer(ch chan int, done chan struct{}) {
for {
select {
case val, ok := <-ch:
if !ok {
done <- struct{}{}
return
}
fmt.Println("消費:", val)
}
}
}select + for:常見循環(huán)寫法
for {
select {
case msg := <-ch:
fmt.Println("收到:", msg)
case <-time.After(5 * time.Second):
fmt.Println("超時退出")
return
}
}select 常見陷阱
| 陷阱 | 描述 |
|---|---|
| 忘記 default 導致阻塞 | 如果所有 case 阻塞,select 也阻塞 |
| 無限阻塞 | select 中所有 channel 永遠不會可用 |
| channel 已關(guān)閉 | 向已關(guān)閉通道寫入會 panic,應特別小心 |
| 超時誤用 | 使用 time.After() 時,不要在循環(huán)里頻繁創(chuàng)建新 channel,否則內(nèi)存泄漏 |
?? 解決建議:使用 time.NewTimer,并復用 timer。
實戰(zhàn)建議
| 建議 | 原因 |
|---|---|
| 用 select 實現(xiàn)超時控制 | 比較優(yōu)雅且非阻塞 |
| select + default 實現(xiàn)非阻塞通信 | 避免 goroutine 卡死 |
| 用 select + context.Done() 控制退出 | 在大型系統(tǒng)中更適合 |
例子:context 控制退出(推薦生產(chǎn)使用)
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
?
ch := make(chan int)
?
go func() {
time.Sleep(2 * time.Second)
ch <- 42
}()
?
select {
case <-ctx.Done():
fmt.Println("操作取消/超時:", ctx.Err())
case val := <-ch:
fmt.Println("接收到數(shù)據(jù):", val)
}總結(jié)
| 維度 | 說明 |
|---|---|
| 功能 | 實現(xiàn) channel 的多路復用監(jiān)聽 |
| 優(yōu)點 | 非阻塞、高效、優(yōu)雅處理通信控制 |
| 結(jié)合 | time.After、context、default 最佳組合 |
| 場景 | goroutine 退出、任務(wù)超時、并發(fā)協(xié)程間通信控制等 |
到此這篇關(guān)于Go語言中select使用的文章就介紹到這了,更多相關(guān)Go select詳解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言學習網(wǎng)絡(luò)編程與Http教程示例
這篇文章主要為大家介紹了Go語言學習網(wǎng)絡(luò)編程與Http教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-03-03
Go語言Http?Server框架實現(xiàn)一個簡單的httpServer
這篇文章主要為大家介紹了Go語言Http?Server框架實現(xiàn)一個簡單的httpServer抽象,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-04-04
解讀rand.Seed(time.Now().UnixNano())的作用及說明
這篇文章主要介紹了關(guān)于rand.Seed(time.Now().UnixNano())的作用及說明,具有很好的參考價值,希望對大家有所幫助。2023-03-03

