淺析Go語言如何在select語句中實(shí)現(xiàn)優(yōu)先級
Go 語言中的 select語句用于監(jiān)控并選擇一組case語句執(zhí)行相應(yīng)的代碼。它看起來類似于switch語句,但是select語句中所有case中的表達(dá)式都必須是channel的發(fā)送或接收操作。一個(gè)典型的select使用示例如下
select {
case <-ch1:
fmt.Println("liwenzhou.com")
case ch2 <- 1:
fmt.Println("q1mi")
}
Go 語言中的 select 關(guān)鍵字也能夠讓當(dāng)前 goroutine 同時(shí)等待ch1 的可讀和ch2的可寫,在ch1和ch2狀態(tài)改變之前,select 會一直阻塞下去,直到其中的一個(gè) channel 轉(zhuǎn)為就緒狀態(tài)時(shí)執(zhí)行對應(yīng)case分支的代碼。如果多個(gè)channel同時(shí)就緒的話則隨機(jī)選擇一個(gè)case執(zhí)行。
除了上面展示的典型示例外,接下來我們逐一介紹一些select的特殊示例。
空select
空select指的是內(nèi)部不包含任何case,例如:
select{
}
空的 select 語句會直接阻塞當(dāng)前的goroutine,使得該goroutine進(jìn)入無法被喚醒的永久休眠狀態(tài)。
只有一個(gè)case
如果select中只包含一個(gè)case,那么該select就變成了一個(gè)阻塞的channel讀/寫操作。
select {
case <-ch1:
fmt.Println("liwenzhou.com")
}
上面的代碼,當(dāng)ch1可讀時(shí)會執(zhí)行打印操作,否則就會阻塞。
有default語句
如果select中還可以包含default語句,用于當(dāng)其他case都不滿足時(shí)執(zhí)行一些默認(rèn)操作。
select {
case <-ch1:
fmt.Println("liwenzhou.com")
default:
time.Sleep(time.Second)
}
上面的代碼,當(dāng)ch1可讀時(shí)會執(zhí)行打印操作,否則就執(zhí)行default語句中的代碼,這里就相當(dāng)于做了一個(gè)非阻塞的channel讀取操作。
總結(jié)
select不存在任何的case:永久阻塞當(dāng)前 goroutineselect只存在一個(gè)case:阻塞的發(fā)送/接收select存在多個(gè)case:隨機(jī)選擇一個(gè)滿足條件的case執(zhí)行select存在default,其他case都不滿足時(shí):執(zhí)行default語句中的代碼
如何在select中實(shí)現(xiàn)優(yōu)先級
已知,當(dāng)select 存在多個(gè) case時(shí)會隨機(jī)選擇一個(gè)滿足條件的case執(zhí)行。
現(xiàn)在我們有一個(gè)需求:我們有一個(gè)函數(shù)會持續(xù)不間斷地從ch1和ch2中分別接收任務(wù)1和任務(wù)2, 如何確保當(dāng)ch1和ch2同時(shí)達(dá)到就緒狀態(tài)時(shí),優(yōu)先執(zhí)行任務(wù)1,在沒有任務(wù)1的時(shí)候再去執(zhí)行任務(wù)2呢?
高級Go語言程序員小明撓了撓頭寫出了如下函數(shù):
func worker(ch1, ch2 <-chan int, stopCh chan struct{}) {
for {
select {
case <-stopCh:
return
case job1 := <-ch1:
fmt.Println(job1)
default:
select {
case job2 := <-ch2:
fmt.Println(job2)
default:
}
}
}
}
上面的代碼通過嵌套兩個(gè)select實(shí)現(xiàn)了"優(yōu)先級",看起來是滿足題目要求的。但是這代碼有點(diǎn)問題,如果ch1和ch2都沒有達(dá)到就緒狀態(tài)的話,整個(gè)程序不會阻塞而是進(jìn)入了死循環(huán)。
怎么辦呢?
小明又撓了撓頭,又寫下了另一個(gè)解決方案:
func worker2(ch1, ch2 <-chan int, stopCh chan struct{}) {
for {
select {
case <-stopCh:
return
case job1 := <-ch1:
fmt.Println(job1)
case job2 := <-ch2:
priority:
for {
select {
case job1 := <-ch1:
fmt.Println(job1)
default:
break priority
}
}
fmt.Println(job2)
}
}
}
這一次,小明不僅使用了嵌套的select,還組合使用了for循環(huán)和LABEL來實(shí)現(xiàn)題目的要求。上面的代碼在外層select選中執(zhí)行job2 := <-ch2時(shí),進(jìn)入到內(nèi)層select循環(huán)繼續(xù)嘗試執(zhí)行job1 := <-ch1,當(dāng)ch1就緒時(shí)就會一直執(zhí)行,否則跳出內(nèi)層select。
實(shí)際應(yīng)用場景
上面的需求雖然是我編的,但是關(guān)于在select中實(shí)現(xiàn)優(yōu)先級在實(shí)際生產(chǎn)中是有實(shí)際應(yīng)用場景的,例如K8s的controller中就有關(guān)于上面這個(gè)技巧的實(shí)際使用示例,這里在關(guān)于select中實(shí)現(xiàn)優(yōu)先級相關(guān)代碼的關(guān)鍵處都已添加了注釋,具體邏輯這里就不展開細(xì)說了。
// kubernetes/pkg/controller/nodelifecycle/scheduler/taint_manager.go
func (tc *NoExecuteTaintManager) worker(worker int, done func(), stopCh <-chan struct{}) {
defer done()
// 當(dāng)處理具體事件的時(shí)候,我們會希望 Node 的更新操作優(yōu)先于 Pod 的更新
// 因?yàn)?NodeUpdates 與 NoExecuteTaintManager無關(guān)應(yīng)該盡快處理
// -- 我們不希望用戶(或系統(tǒng))等到PodUpdate隊(duì)列被耗盡后,才開始從受污染的Node中清除pod。
for {
select {
case <-stopCh:
return
case nodeUpdate := <-tc.nodeUpdateChannels[worker]:
tc.handleNodeUpdate(nodeUpdate)
tc.nodeUpdateQueue.Done(nodeUpdate)
case podUpdate := <-tc.podUpdateChannels[worker]:
// 如果我們發(fā)現(xiàn)了一個(gè) Pod 需要更新,我么你需要先清空 Node 隊(duì)列.
priority:
for {
select {
case nodeUpdate := <-tc.nodeUpdateChannels[worker]:
tc.handleNodeUpdate(nodeUpdate)
tc.nodeUpdateQueue.Done(nodeUpdate)
default:
break priority
}
}
// 在 Node 隊(duì)列清空后我們再處理 podUpdate.
tc.handlePodUpdate(podUpdate)
tc.podUpdateQueue.Done(podUpdate)
}
}
}
到此這篇關(guān)于淺析Go語言如何在select語句中實(shí)現(xiàn)優(yōu)先級的文章就介紹到這了,更多相關(guān)Go select優(yōu)先級內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言使用sort包對任意類型元素的集合進(jìn)行排序的方法
這篇文章主要介紹了Go語言使用sort包對任意類型元素的集合進(jìn)行排序的方法,實(shí)例分析了sort排序所涉及的方法與相關(guān)的使用技巧,需要的朋友可以參考下2015-02-02
Go語言學(xué)習(xí)教程之結(jié)構(gòu)體的示例詳解
結(jié)構(gòu)體是一個(gè)序列,包含一些被命名的元素,這些被命名的元素稱為字段(field),每個(gè)字段有一個(gè)名字和一個(gè)類型。本文通過一些示例帶大家深入了解Go語言中結(jié)構(gòu)體的使用,需要的可以參考一下2022-09-09
gorm golang 并發(fā)連接數(shù)據(jù)庫報(bào)錯(cuò)的解決方法
今天小編就為大家分享一篇gorm golang 并發(fā)連接數(shù)據(jù)庫報(bào)錯(cuò)的解決方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-07-07
Golang實(shí)現(xiàn)獲取與解析命令行參數(shù)
這篇文章主要為大家詳細(xì)介紹了Golang如何實(shí)現(xiàn)獲取與解析命令行參數(shù),文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的小伙伴可以參考一下2024-01-01
Go語言中服務(wù)網(wǎng)格Istio實(shí)戰(zhàn)
本文主要介紹了Go語言中服務(wù)網(wǎng)格Istio實(shí)戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2026-04-04

