golang控制結(jié)構(gòu)select機(jī)制及使用示例詳解
GO select
在 Go 語(yǔ)言中,select 是一種用于處理多個(gè)通道操作的控制結(jié)構(gòu)。它可以用于在多個(gè)通道之間進(jìn)行非阻塞的選擇操作。
select 語(yǔ)句由一系列的 case 子句組成,每個(gè) case 子句表示一個(gè)通道操作。select 語(yǔ)句會(huì)按照順序依次檢查每個(gè) case 子句,并執(zhí)行其中可執(zhí)行的操作。
select 的作用主要有以下幾個(gè)方面:
多路復(fù)用通道
select 可以同時(shí)監(jiān)聽(tīng)多個(gè)通道上的操作,一旦某個(gè)通道可讀或可寫(xiě),就會(huì)執(zhí)行相應(yīng)的操作。這樣可以避免使用阻塞的 channel 操作,提高程序的并發(fā)性能。
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go func() {
time.Sleep(2 * time.Second)
ch1 <- 1
}()
go func() {
time.Sleep(1 * time.Second)
ch2 <- 2
}()
select {
case <-ch1:
fmt.Println("Received from ch1")
case <-ch2:
fmt.Println("Received from ch2")
case <-time.After(3 * time.Second):
fmt.Println("Timeout")
}
}在這個(gè)示例中,我們創(chuàng)建了兩個(gè)通道 ch1 和 ch2。然后分別在兩個(gè) goroutine 中進(jìn)行操作,通過(guò)不同的延遲時(shí)間向通道發(fā)送數(shù)據(jù)。
在 main 函數(shù)中,我們使用 select 語(yǔ)句同時(shí)監(jiān)聽(tīng) ch1 和 ch2 兩個(gè)通道,并通過(guò) <-ch1 和 <-ch2 分別接收通道中的數(shù)據(jù)。同時(shí),我們還使用 time.After 函數(shù)設(shè)置了一個(gè) 3 秒的超時(shí)時(shí)間。
在 select 語(yǔ)句的執(zhí)行過(guò)程中,會(huì)依次檢查每個(gè) case 子句。如果有多個(gè) case 子句都是可執(zhí)行的,select 會(huì)隨機(jī)選擇一個(gè)執(zhí)行。在這個(gè)示例中,由于 ch2 的數(shù)據(jù)發(fā)送時(shí)間比 ch1 早,所以最終會(huì)執(zhí)行 case <-ch2 分支,輸出 "Received from ch2"。
如果 select 語(yǔ)句中的所有通道都沒(méi)有數(shù)據(jù)可讀,并且超過(guò)了設(shè)置的超時(shí)時(shí)間,那么就會(huì)執(zhí)行 time.After 對(duì)應(yīng)的 case 分支,輸出 "Timeout"。
非阻塞的通道操作
select 語(yǔ)句中的 case 子句可以使用非阻塞的通道操作,包括發(fā)送和接收操作。如果沒(méi)有可用的通道操作,select 會(huì)立即執(zhí)行 default 子句(如果有),或者阻塞等待第一個(gè)可執(zhí)行的操作。
package main
import (
"fmt"
)
func main() {
ch := make(chan int, 2)
ch <- 1 // 向通道寫(xiě)入數(shù)據(jù),此時(shí)通道未滿,操作不會(huì)被阻塞
fmt.Println("Data written to channel")
select {
case ch <- 2: // 嘗試向已滿的通道再次寫(xiě)入數(shù)據(jù),由于通道已滿,操作會(huì)被立即返回
fmt.Println("Data written to channel")
default:
fmt.Println("Channel is full, unable to write data")
}
data, ok := <-ch // 嘗試從通道讀取數(shù)據(jù),此時(shí)通道中有數(shù)據(jù),操作不會(huì)被阻塞
if ok {
fmt.Println("Data read from channel:", data)
}
select {
case data, ok := <-ch: // 嘗試從空的通道讀取數(shù)據(jù),由于通道為空,操作會(huì)被立即返回
if ok {
fmt.Println("Data read from channel:", data)
} else {
fmt.Println("Channel is empty, unable to read data")
}
default:
fmt.Println("Channel is empty, unable to read data")
}
}在這個(gè)示例中,我們首先創(chuàng)建了一個(gè)緩沖大小為 2 的通道 ch。然后,我們使用帶緩沖的通道進(jìn)行數(shù)據(jù)寫(xiě)入操作 ch <- 1,由于通道未滿,操作不會(huì)被阻塞。
接下來(lái),我們使用非阻塞的通道寫(xiě)入操作 ch <- 2,由于通道已滿,操作會(huì)立即返回。我們使用 select 語(yǔ)句來(lái)處理這種情況,當(dāng)無(wú)法進(jìn)行通道寫(xiě)入操作時(shí),會(huì)執(zhí)行 default 分支,輸出 "Channel is full, unable to write data"。
然后,我們嘗試從通道中讀取數(shù)據(jù) data, ok := <-ch,由于通道中有數(shù)據(jù),操作不會(huì)被阻塞。
最后,我們使用非阻塞的通道讀取操作 data, ok := <-ch,由于通道為空,操作會(huì)立即返回。同樣,我們使用 select 語(yǔ)句來(lái)處理這種情況,當(dāng)無(wú)法進(jìn)行通道讀取操作時(shí),會(huì)執(zhí)行 default 分支,輸出 "Channel is empty, unable to read data"。
超時(shí)處理
通過(guò)在 select 語(yǔ)句中結(jié)合使用 time.After 函數(shù)和通道操作,可以實(shí)現(xiàn)超時(shí)機(jī)制。例如,可以使用 select 監(jiān)聽(tīng)一個(gè)帶有超時(shí)的通道操作,當(dāng)超過(guò)指定時(shí)間時(shí),執(zhí)行相應(yīng)的操作。
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
go func() {
time.Sleep(2 * time.Second)
ch <- 1
}()
select {
case <-ch:
fmt.Println("Received from channel")
case <-time.After(3 * time.Second):
fmt.Println("Timeout")
}
}在這個(gè)示例中,我們創(chuàng)建了一個(gè)通道 ch。然后,我們?cè)谝粋€(gè) goroutine 中進(jìn)行操作,在 2 秒后向通道發(fā)送數(shù)據(jù) ch <- 1。
在 main 函數(shù)中,我們使用 select 語(yǔ)句同時(shí)監(jiān)聽(tīng) ch 通道和 time.After 函數(shù)返回的超時(shí)通道。超時(shí)通道是一個(gè)計(jì)時(shí)器通道,在指定的時(shí)間后會(huì)發(fā)送一個(gè)值給通道。
在 select 語(yǔ)句的執(zhí)行過(guò)程中,會(huì)依次檢查每個(gè) case 子句。如果 ch 通道接收到了數(shù)據(jù),就會(huì)執(zhí)行 case <-ch 分支,輸出 "Received from channel"。如果等待時(shí)間超過(guò)了設(shè)定的超時(shí)時(shí)間(這里是 3 秒),就會(huì)執(zhí)行 time.After 對(duì)應(yīng)的 case 分支,輸出 "Timeout"。
在這個(gè)示例中,由于通道的發(fā)送操作需要 2 秒才能完成,而超時(shí)時(shí)間設(shè)定為 3 秒,所以最終會(huì)執(zhí)行 case <-ch 分支,輸出 "Received from channel"。
控制并發(fā)流程
select 可以與 goroutine 結(jié)合使用,實(shí)現(xiàn)對(duì)并發(fā)流程的控制。通過(guò)在 select 中使用通道操作來(lái)進(jìn)行同步或通信,可以協(xié)調(diào)不同 goroutine 之間的執(zhí)行順序。
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
// 設(shè)置并發(fā)任務(wù)數(shù)量
concurrency := 3
// 創(chuàng)建一個(gè)用于控制并發(fā)的通道
semaphore := make(chan struct{}, concurrency)
// 假設(shè)有一組任務(wù)需要并發(fā)執(zhí)行
tasks := []string{"task1", "task2", "task3", "task4", "task5"}
// 遍歷任務(wù)列表
for _, task := range tasks {
// 增加 WaitGroup 的計(jì)數(shù)器
wg.Add(1)
// 啟動(dòng)一個(gè) goroutine 來(lái)執(zhí)行任務(wù)
go func(t string) {
// 在 goroutine 開(kāi)始前向通道發(fā)送一個(gè)信號(hào)
semaphore <- struct{}{}
// 執(zhí)行任務(wù)
fmt.Println("Executing", t)
// 模擬任務(wù)執(zhí)行時(shí)間
// 這里可以是任何實(shí)際的任務(wù)邏輯
// ...
// 任務(wù)完成后從通道釋放一個(gè)信號(hào)
<-semaphore
// 減少 WaitGroup 的計(jì)數(shù)器
wg.Done()
}(task)
}
// 等待所有任務(wù)完成
wg.Wait()
fmt.Println("All tasks completed")
}在這個(gè)示例中,我們首先定義了并發(fā)任務(wù)的數(shù)量 concurrency,這決定了同時(shí)執(zhí)行任務(wù)的最大數(shù)量。然后,我們創(chuàng)建了一個(gè)用于控制并發(fā)的通道 semaphore,通過(guò)向通道發(fā)送信號(hào)來(lái)控制并發(fā)數(shù)量。
接下來(lái),我們定義了一組需要并發(fā)執(zhí)行的任務(wù)列表 tasks。在遍歷任務(wù)列表時(shí),我們?cè)黾恿?nbsp;WaitGroup 的計(jì)數(shù)器,并啟動(dòng)一個(gè) goroutine 來(lái)執(zhí)行每個(gè)任務(wù)。
在每個(gè)任務(wù)的 goroutine 中,首先向通道 semaphore 發(fā)送一個(gè)信號(hào),以占用一個(gè)并發(fā)槽位。然后執(zhí)行任務(wù)的邏輯,這里使用了簡(jiǎn)單的輸出來(lái)表示任務(wù)的執(zhí)行。任務(wù)執(zhí)行完畢后,從通道 semaphore 中釋放一個(gè)信號(hào),以讓其他任務(wù)可以占用并發(fā)槽位。最后,減少 WaitGroup 的計(jì)數(shù)器,表示任務(wù)完成。
最后,我們使用 WaitGroup 的 Wait 方法來(lái)等待所有任務(wù)完成,確保程序在所有任務(wù)執(zhí)行完畢后再繼續(xù)執(zhí)行。
總結(jié)
以下是 select 語(yǔ)句的一些特性:
- 如果沒(méi)有任何通道操作準(zhǔn)備好,且沒(méi)有默認(rèn)的
case子句,那么select語(yǔ)句會(huì)被阻塞,直到至少有一個(gè)通道操作準(zhǔn)備好。 - 如果有多個(gè)
case子句準(zhǔn)備好,那么會(huì)隨機(jī)選擇一個(gè)執(zhí)行。不會(huì)有優(yōu)先級(jí)或順序的保證。 select語(yǔ)句可以用于發(fā)送和接收操作,也可以混合使用。select語(yǔ)句可以與for循環(huán)結(jié)合使用,以實(shí)現(xiàn)對(duì)多個(gè)通道的連續(xù)監(jiān)控和處理。
select 機(jī)制是 Golang 中處理并發(fā)操作的重要工具之一,它能夠很好地處理多個(gè)通道操作,避免阻塞和死鎖的問(wèn)題。
以上就是golang控制結(jié)構(gòu)select機(jī)制使用示例詳解的詳細(xì)內(nèi)容,更多關(guān)于golang select機(jī)制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
go語(yǔ)言實(shí)現(xiàn)二叉樹(shù)的序例化與反序列化
這篇文章主要介紹了go語(yǔ)言實(shí)現(xiàn)二叉樹(shù)的序例化與反序列化,文章圍繞主題展開(kāi)詳細(xì)內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-05-05
一文詳解golang延時(shí)任務(wù)的實(shí)現(xiàn)
這篇文章主要為大家介紹了golang延時(shí)任務(wù)的實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
Go單元測(cè)試對(duì)GORM進(jìn)行Mock測(cè)試
這篇文章主要為大家介紹了Go單元測(cè)試對(duì)GORM進(jìn)行Mock測(cè)試用例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
Go語(yǔ)言中:= 運(yùn)算符短變量聲明的使用小結(jié)
本文全面解析Go語(yǔ)言特有的:=運(yùn)算符,該運(yùn)算符兼具變量聲明和初始化功能,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-11-11
go語(yǔ)言定時(shí)器Timer及Ticker的功能使用示例詳解
這篇文章主要為大家介紹了go語(yǔ)言定時(shí)器的功能使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04
Golang通過(guò)SSH執(zhí)行交換機(jī)操作實(shí)現(xiàn)
這篇文章主要介紹了Golang通過(guò)SSH執(zhí)行交換機(jī)操作實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
Go類(lèi)型斷言提取測(cè)試接口值動(dòng)態(tài)類(lèi)型及靜態(tài)轉(zhuǎn)換確保類(lèi)型接口編譯安全
這篇文章主要為大家介紹了Go類(lèi)型斷言提取測(cè)試接口值動(dòng)態(tài)類(lèi)型及靜態(tài)轉(zhuǎn)換確保類(lèi)型實(shí)現(xiàn)特定接口的編譯時(shí)安全性詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10

