最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go 語言 select 的實(shí)現(xiàn)原理解析

 更新時(shí)間:2025年01月19日 09:46:05   作者:不7夜宵  
select是Go在語言層面提供的I/O多路復(fù)用的機(jī)制,其專門用來讓Goroutine同時(shí)等待多個(gè)channel是否準(zhǔn)備完畢:可讀或可寫,這篇文章主要介紹了Go 語言 select 的實(shí)現(xiàn)原理,需要的朋友可以參考下

介紹

select是Go在語言層面提供的I/O多路復(fù)用的機(jī)制,其專門用來讓Goroutine同時(shí)等待多個(gè)channel是否準(zhǔn)備完畢:可讀或可寫。在Channel狀態(tài)改變之前,select會一直阻塞當(dāng)前線程或者goroutine。

特性:

case 必須是一個(gè)通信操作,主要是指對通道(Channel)進(jìn)行發(fā)送或者接收數(shù)據(jù)的操作。

select 語句中除 default 外,各 case 執(zhí)行順序是隨機(jī)的。

select 語句中如果沒有 default 語句,則會阻塞等待任意一個(gè) case滿足執(zhí)行條件。

select 語句中除 default 外,每個(gè) case 只能操作一個(gè) channel,要么讀要么寫。

當(dāng) select 中的多個(gè) case 同時(shí)被觸發(fā)時(shí),會隨機(jī)執(zhí)行其中的一個(gè)。

普通多線程

多路復(fù)用

 數(shù)據(jù)結(jié)構(gòu)

select在Go語言的源代碼中不存在對應(yīng)的結(jié)構(gòu)體,使用runtime.scase 結(jié)構(gòu)體表示select控制結(jié)構(gòu)里的case。

type scase struct {
	c    *hchan                    //case操作的通道     
    kind  uint16
    //表示該case的類型,分為讀channel、寫channel和default。
    //讀channel、寫channel和default三種類型分別由常量定義
    //caseRecv:case語句中嘗試讀取scase.c中的數(shù)據(jù)。
    //caseSend:case語句中嘗試向scase.c中寫入數(shù)據(jù)。
    //caseDefault:default語句。
	elem unsafe.Pointer 
    //scase.kind == caseRecv : scase.elem表示讀出channel的數(shù)據(jù)存放地址;
    //scase.kind == caseSend : scase.elem表示將要寫入channel的數(shù)據(jù)存放地址;
}

在select語句運(yùn)行時(shí),scase結(jié)構(gòu)體的實(shí)例會被用來表示每個(gè)case。運(yùn)行時(shí)根據(jù)c 字段找到對應(yīng)的通道,根據(jù)elem字段來處理數(shù)據(jù)的發(fā)送或接收操作。

執(zhí)行流程

 實(shí)現(xiàn)過程和結(jié)果(穿插編譯器的重寫和優(yōu)化)

單分支的select

只有一個(gè) case 且不是 default,這種情況編譯器會直接將其翻譯成對管道的收發(fā)操作,并且還是阻塞式的,一直阻塞到操作可以完成。

對于接收操作(如 case val := <-ch),它會被轉(zhuǎn)換為 val := <-ch,直接嘗試從通道 ch 接收數(shù)據(jù)。

對于發(fā)送操作(如 case ch <- value),它會被轉(zhuǎn)換為 ch <- value,直接嘗試向通道 ch 發(fā)送數(shù)據(jù)。

只包含default分支會直接執(zhí)行default操作。

多路select

在編譯器中會被轉(zhuǎn)換為runtime.selectgo函數(shù)調(diào)用。

func selectgo(cas0 *scase, order0 *uint16, , ncases int) (int, bool) {
    pollorder := order1[:ncases:ncases]
    lockorder := order1[ncases:][:ncases:ncases]
    for i := 1; i < ncases; i++ {
        j := fastrandn(uint32(i + 1))
        pollorder[i] = pollorder[j]
        pollorder[j] = uint16(i)
    }
    // 代碼可能繼續(xù)執(zhí)行后續(xù)操作
}
  • cas0,scase數(shù)組的頭部指針,前半部分存放的是寫管道 case,后半部分存放的讀管道 case,以nsends來區(qū)分
  • order0,它的長度是scase數(shù)組的兩倍,前半部分分配給pollorder數(shù)組(決定管道執(zhí)行順序),后半部分分配給lockorder數(shù)組(決定管道鎖定順序)
  • pollorder:每次selectgo執(zhí)行都會把scase序列打亂,以達(dá)到隨機(jī)檢測case的目的。
  • lockorder:所有case語句中channel序列,以達(dá)到去重防止對channel加鎖時(shí)重復(fù)加鎖的目的。
  • ncases表示scase數(shù)組的長度

直接阻塞

1. select結(jié)構(gòu)不包含任何case

在Go編譯器內(nèi)部的代碼如下

func walkselectcases(cases *Nodes) []*Node {
	n := cases.Len()
	if n == 0 {
		return []*Node{mkcall("block", nil, nil)}
	}
	...
}
func block() {
	gopark(nil, nil, waitReasonSelectNoCases, traceEvGoStop, 1)
}

walkselectcases的參數(shù)是一個(gè)select語句中的case元素的集合。當(dāng)集合的長度為0時(shí),表示當(dāng)前select中無case會調(diào)用block函數(shù),block函數(shù)會調(diào)用gopark讓出goroutine對處理器的使用權(quán)并傳入等待原因,暫停goroutine避免CPU空轉(zhuǎn)。

2.當(dāng)case中的channel是空指針

例:包含一個(gè)case且case中的channel是空指針,編譯器會將select改寫為if條件語句

//部分代碼
if ch == nil {
    block()調(diào)用block,將goroutine陷入永久休眠
}

非阻塞操作 

當(dāng)select中包含default分支時(shí),就會被編譯器認(rèn)為是一次非阻塞的收發(fā)操作。

示例:一個(gè)case分支一個(gè)default分支,對通道的讀寫操作

示例:一個(gè)case分支一個(gè)default分支,對通道的讀寫操作

寫操作:編譯器會使用條件語句和 runtime.selectnbsend 函數(shù)改寫代碼

//改寫
if selectnbsend(ch, i) {
    ...
} else {
    ...
}
//false參數(shù)決定了這一次的發(fā)送是非阻塞的,所以如果存在緩沖區(qū)空間不足時(shí),當(dāng)前 Goroutine 都不會阻塞而是會直接返回。
func selectnbsend(c *hchan, elem unsafe.Pointer) (selected bool) {
	return chansend(c, elem, false, getcallerpc())
}

 讀操作:

// 改寫前
select {
case v <- ch: // case v, ok <- ch:
    ......
default:
    ......
}
// 改寫后
if selectnbrecv(&v, ch) { // if selectnbrecv2(&v, &ok, ch) {
    ...
} else {
    ...
}
//看讀操作是否需要,第一個(gè)會忽略返回的布爾值,第二個(gè)會將布爾值傳給調(diào)用方,block參數(shù)決定本次操作不阻塞
func selectnbrecv(elem unsafe.Pointer, c *hchan) (selected bool) {
	selected, _ = chanrecv(c, elem, false)
	return
}
func selectnbrecv2(elem unsafe.Pointer, received *bool, c *hchan) (selected bool) {
	selected, *received = chanrecv(c, elem, false)
	return
}

 性能優(yōu)化建議

  • case數(shù)量控制建議不超過5-10個(gè)
  • 適當(dāng)使用帶緩沖區(qū)的channel避免頻繁的阻塞和喚醒
  • 合理使用default避免無謂的阻塞

到此這篇關(guān)于Go 語言 select 的實(shí)現(xiàn)原理的文章就介紹到這了,更多相關(guān)Go 語言 select內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go中最強(qiáng)大的權(quán)限控制庫(Casbin)的實(shí)現(xiàn)

    Go中最強(qiáng)大的權(quán)限控制庫(Casbin)的實(shí)現(xiàn)

    Casbin是一個(gè)強(qiáng)大的、高效的開源訪問控制框架,支持ACL、RBAC、ABAC 等多種經(jīng)典訪問控制模型,通過配置文件即可靈活定義權(quán)限規(guī)則,具有一定的參考價(jià)值,感興趣的可以了解一下
    2026-03-03
  • go語言之go(goroutine)控制異步詳解

    go語言之go(goroutine)控制異步詳解

    Go語言通過goroutine實(shí)現(xiàn)并發(fā),允許異步執(zhí)行函數(shù),但單獨(dú)使用會引發(fā)順序問題,需結(jié)合WaitGroup確保主線程等待所有并發(fā)任務(wù)完成后再輸出結(jié)果,從而正確同步執(zhí)行流程
    2025-07-07
  • go語言單例模式(Singleton)實(shí)例分析

    go語言單例模式(Singleton)實(shí)例分析

    這篇文章主要介紹了go語言單例模式(Singleton),實(shí)例分析了單例模式的原理與Go語言的實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2015-03-03
  • golang中使用匿名結(jié)構(gòu)體的方法

    golang中使用匿名結(jié)構(gòu)體的方法

    這篇文章主要介紹了golang中使用匿名結(jié)構(gòu)體,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-08-08
  • 最新評論

    江陵县| 门头沟区| 平罗县| 旅游| 朝阳区| 康平县| 香河县| 花垣县| 萍乡市| 峨山| 永德县| 青田县| 海南省| 高青县| 岑巩县| 炉霍县| 丰宁| 武强县| 七台河市| 新干县| 宝应县| 灌云县| 灵丘县| 唐海县| 琼结县| 临澧县| 靖宇县| 临洮县| 宝丰县| 深州市| 会泽县| 朝阳市| 水富县| 五常市| 鄂州市| 琼中| 柞水县| 铁力市| 拉萨市| 桃园县| 兴业县|