真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

go語言select go語言select源碼解析

在go語言中select關(guān)鍵字怎么用?

select 語句使得一個 goroutine 在多個通訊操作上等待。

湖南網(wǎng)站建設(shè)公司創(chuàng)新互聯(lián),湖南網(wǎng)站設(shè)計制作,有大型網(wǎng)站制作公司豐富經(jīng)驗。已為湖南近千家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\外貿(mào)營銷網(wǎng)站建設(shè)要多少錢,請找那個售后服務(wù)好的湖南做網(wǎng)站的公司定做!

select 會阻塞,直到條件分支中的某個可以繼續(xù)執(zhí)行,這時就會執(zhí)行那個條件分支。當(dāng)多個都準(zhǔn)備好的時候,會隨機(jī)選擇一個。

復(fù)制代碼代碼如下:

package main

import "fmt"

func fibonacci(c, quit chan int) {

x, y := 1, 1

for {

select {

case c - x:

x, y = y, x + y

case -quit:

fmt.Println("quit")

return

}

}

}

func main() {

c := make(chan int)

quit := make(chan int)

go func() {

for i := 0; i 10; i++ {

fmt.Println(-c)

}

quit - 0

}()

fibonacci(c, quit)

}

默認(rèn)選擇

當(dāng) select 中的其他條件分支都沒有準(zhǔn)備好的時候,default 分支會被執(zhí)行。

為了非阻塞的發(fā)送或者接收,可使用 default 分支:

select {

case i := -c:

// use i

default:

// receiving from c would block

}

復(fù)制代碼代碼如下:

package main

import (

"fmt"

"time"

)

func main() {

tick := time.Tick(1e8)

boom := time.After(5e8)

for {

select {

case -tick:

fmt.Println("tick.")

case -boom:

fmt.Println("BOOM!")

return

default:

fmt.Println(" .")

time.Sleep(5e7)

}

}

}

golang select 為什么要for循環(huán)

有數(shù)量不定的goroutine往channel里塞東西,然后select來接收并處理。如果所有的goroutine都完成工作,ch也接收完了,那么select就會阻塞?,F(xiàn)在我想要跳出死循環(huán),大概是在for循環(huán)里設(shè)置一些東西,不知道可不可以實現(xiàn),或者有類似的解決方法。

go func(){ for{ select{ case v:= 《-ch: //這里打左尖括號排版就會亂,不知道是不是網(wǎng)站的bug DoSomething() } } }()

go語言select的作用

Go里面提供了一個關(guān)鍵字select,通過select可以監(jiān)聽channel上的數(shù)據(jù)流動。

select的用法與switch語言非常類似,由select開始一個新的選擇塊,每個選擇條件由case語句來描述。

與switch語句相比, select有比較多的限制,其中最大的一條限制就是每個case語句里必須是一個IO操作,大致的結(jié)構(gòu)如下:

在一個select語句中,Go語言會按順序從頭至尾評估每一個發(fā)送和接收的語句。

如果其中的任意一語句可以繼續(xù)執(zhí)行(即沒有被阻塞),那么就從那些可以執(zhí)行的語句中任意選擇一條來使用。

如果沒有任意一條語句可以執(zhí)行(即所有的通道都被阻塞),那么有兩種可能的情況:

如果給出了default語句,那么就會執(zhí)行default語句,同時程序的執(zhí)行會從select語句后的語句中恢復(fù)。

如果沒有default語句,那么select語句將被阻塞,直到至少有一個通信可以進(jìn)行下去

有時候會出現(xiàn)goroutine阻塞的情況,那么我們?nèi)绾伪苊庹麄€程序進(jìn)入阻塞的情況呢?我們可以利用select來設(shè)置超時,通過如下的方式實現(xiàn):

select總結(jié):

作用: 用來監(jiān)聽 channel 上的數(shù)據(jù)流動方向。 讀?寫?

select實現(xiàn)fibonacci數(shù)列:

圖解Go中select語句的底層原理

Go 的select語句是一種僅能用于channl發(fā)送和接收消息的專用語句,此語句運行期間是阻塞的;當(dāng)select中沒有case語句的時候,會阻塞當(dāng)前的groutine。所以,有人也會說select是用來阻塞監(jiān)聽goroutine的。

還有人說:select是Golang在語言層面提供的I/O多路復(fù)用的機(jī)制,其專門用來檢測多個channel是否準(zhǔn)備完畢:可讀或可寫。

以上說法都正確。

我們來回顧一下是什么是 I/O多路復(fù)用 。

每來一個進(jìn)程,都會建立連接,然后阻塞,直到接收到數(shù)據(jù)返回響應(yīng)。

普通這種方式的缺點其實很明顯:系統(tǒng)需要創(chuàng)建和維護(hù)額外的線程或進(jìn)程。因為大多數(shù)時候,大部分阻塞的線程或進(jìn)程是處于等待狀態(tài),只有少部分會接收并處理響應(yīng),而其余的都在等待。系統(tǒng)為此還需要多做很多額外的線程或者進(jìn)程的管理工作。

為了解決圖中這些多余的線程或者進(jìn)程,于是有了"I/O多路復(fù)用"

每個線程或者進(jìn)程都先到圖中”裝置“中注冊,然后阻塞,然后只有一個線程在”運輸“,當(dāng)注冊的線程或者進(jìn)程準(zhǔn)備好數(shù)據(jù)后,”裝置“會根據(jù)注冊的信息得到相應(yīng)的數(shù)據(jù)。從始至終kernel只會使用圖中這個黃黃的線程,無需再對額外的線程或者進(jìn)程進(jìn)行管理,提升了效率。

select的實現(xiàn)經(jīng)歷了多個版本的修改,當(dāng)前版本為:1.11

select這個語句底層實現(xiàn)實際上主要由兩部分組成: case語句 和 執(zhí)行函數(shù) 。

源碼地址為:/go/src/runtime/select.go

每個case語句,單獨抽象出以下結(jié)構(gòu)體:

結(jié)構(gòu)體可以用下圖表示:

然后執(zhí)行select語句實際上就是調(diào)用 func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) 函數(shù)。

func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) 函數(shù)參數(shù):

selectgo 返回所選scase的索引(該索引與其各自的select {recv,send,default}調(diào)用的序號位置相匹配)。此外,如果選擇的scase是接收操作(recv),則返回是否接收到值。

誰負(fù)責(zé)調(diào)用 func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) 函數(shù)呢?

在 /reflect/value.go 中有個 func rselect([]runtimeSelect) (chosen int, recvOK bool) 函數(shù),此函數(shù)的實現(xiàn)在 /runtime/select.go 文件中的 func reflect_rselect(cases []runtimeSelect) (int, bool) 函數(shù)中:

那誰調(diào)用的 func rselect([]runtimeSelect) (chosen int, recvOK bool) 呢?

在 /refect/value.go 中,有一個 func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool) 的函數(shù),其調(diào)用了 rselect 函數(shù),并將最終Go中select語句的返回值的返回。

以上這三個函數(shù)的調(diào)用棧按順序如下:

這仨函數(shù)中無論是返回值還是參數(shù)都大同小異,可以簡單粗暴的認(rèn)為:函數(shù)參數(shù)傳入的是case語句,返回值返回被選中的case語句。

那誰調(diào)用了 func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool) 呢?

可以簡單的認(rèn)為是系統(tǒng)了。

來個簡單的圖:

前兩個函數(shù) Select 和 rselect 都是做了簡單的初始化參數(shù),調(diào)用下一個函數(shù)的操作。select真正的核心功能,是在最后一個函數(shù) func selectgo(cas0 *scase, order0 *uint16, ncases int) (int, bool) 中實現(xiàn)的。

打亂傳入的case結(jié)構(gòu)體順序

鎖住其中的所有的channel

遍歷所有的channel,查看其是否可讀或者可寫

如果其中的channel可讀或者可寫,則解鎖所有channel,并返回對應(yīng)的channel數(shù)據(jù)

假如沒有channel可讀或者可寫,但是有default語句,則同上:返回default語句對應(yīng)的scase并解鎖所有的channel。

假如既沒有channel可讀或者可寫,也沒有default語句,則將當(dāng)前運行的groutine阻塞,并加入到當(dāng)前所有channel的等待隊列中去。

然后解鎖所有channel,等待被喚醒。

此時如果有個channel可讀或者可寫ready了,則喚醒,并再次加鎖所有channel,

遍歷所有channel找到那個對應(yīng)的channel和G,喚醒G,并將沒有成功的G從所有channel的等待隊列中移除。

如果對應(yīng)的scase值不為空,則返回需要的值,并解鎖所有channel

如果對應(yīng)的scase為空,則循環(huán)此過程。

在想想select和channel做了什么事兒,我覺得和多路復(fù)用是一回事兒


當(dāng)前名稱:go語言select go語言select源碼解析
當(dāng)前鏈接:http://weahome.cn/article/hhjspj.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部