新聞中心
01 介紹
Go 協(xié)程之間通過(guò) channel 通信,但是 channel 讀寫(xiě)取決于自身特性,即是否有可寫(xiě)入緩沖區(qū)、緩沖區(qū)中是否有數(shù)據(jù)、是否已關(guān)閉...

我們提供的服務(wù)有:成都做網(wǎng)站、成都網(wǎng)站建設(shè)、微信公眾號(hào)開(kāi)發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、益陽(yáng)ssl等。為超過(guò)千家企事業(yè)單位解決了網(wǎng)站和推廣的問(wèn)題。提供周到的售前咨詢(xún)和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的益陽(yáng)網(wǎng)站制作公司
為了檢測(cè) channel 的特性,Go 提供了一個(gè)關(guān)鍵字 select,可用于實(shí)現(xiàn) I/O 多路復(fù)用機(jī)制。
本文我們介紹 Go 關(guān)鍵字 select 的使用方式。
02 使用方式
Go 關(guān)鍵字 select 中包含 case 語(yǔ)句和 default 語(yǔ)句,其中 default 語(yǔ)句可以認(rèn)為是一種特殊的 case 語(yǔ)句。
因?yàn)?nbsp;default 語(yǔ)句不負(fù)責(zé)處理 channel 的讀寫(xiě),它可以在 select 中的任意位置,且僅能包含一個(gè) default 語(yǔ)句。在所有 case 語(yǔ)句都不滿(mǎn)足執(zhí)行條件時(shí),default 語(yǔ)句將被執(zhí)行(建議盡量不要省略 default 語(yǔ)句)。
我們通過(guò)代碼片段,分別介紹 select 在檢測(cè)到 channel 不同特性時(shí),得到的運(yùn)行結(jié)果。
空 select
接下來(lái),我們閱讀一段代碼。
func main() {
fmt.Println("Golang 語(yǔ)言開(kāi)發(fā)棧")
go func() {
fmt.Println("Golang 公眾號(hào)")
}()
}閱讀上面這段代碼,讀者朋友們認(rèn)為 Go 協(xié)程中的打印語(yǔ)句可以正常輸出嗎?
讀者朋友們?nèi)绻\(yùn)行代碼,會(huì)發(fā)現(xiàn) Go 協(xié)程中的打印語(yǔ)句還沒(méi)有執(zhí)行,程序就已經(jīng)退出了,這是因?yàn)?nbsp;main 函數(shù)中的打印語(yǔ)句已經(jīng)執(zhí)行完成,所以會(huì)退出程序。
如果我們希望 Go 協(xié)程中的打印語(yǔ)句也執(zhí)行,可以在 main 函數(shù)中使用 select{} 將 main 阻塞,Go 協(xié)程中的打印語(yǔ)句就有機(jī)會(huì)執(zhí)行了。但是,這會(huì)導(dǎo)致死鎖(可以根據(jù)實(shí)際應(yīng)用場(chǎng)景選擇是否使用)。
無(wú)緩沖 channel
接下來(lái),我們?cè)僮x一段可以導(dǎo)致死鎖的代碼:
func main() {
c := make(chan string)
DoChannel(c)
}
func DoChannel(c chan string) {
var receive string
send := "golang"
select {
case receive = <-c:
fmt.Println(receive)
case c <- send:
fmt.Println(send)
}
}閱讀上面這段代碼,我們定義一個(gè)函數(shù) DoChannel(),該函數(shù)接收的參數(shù)是一個(gè) string 類(lèi)型的 channel,函數(shù)體中使用 select 中的兩個(gè) case 語(yǔ)句,分別對(duì)參數(shù)進(jìn)行接收和發(fā)送操作。
運(yùn)行代碼,select 阻塞。
因?yàn)?,我們傳參?nbsp;c 是無(wú)緩沖 channel,所以它即不能讀也不能寫(xiě),兩個(gè) case 語(yǔ)句都不執(zhí)行,select 陷入阻塞,導(dǎo)致死鎖(此處為了行文,故意沒(méi)有 default 語(yǔ)句)。
無(wú)數(shù)據(jù),有緩沖channel
我們將上面這段代碼,稍微修改一下,將入?yún)⒌?nbsp;c 改為 1 個(gè)緩沖區(qū)大小的 channel(未寫(xiě)入數(shù)據(jù))。代碼如下:
func main() {
c := make(chan string, 1)
DoChannel(c)
}運(yùn)行代碼,寫(xiě)執(zhí)行,讀未執(zhí)行。
即 select 中的對(duì)入?yún)?nbsp;channel 進(jìn)行發(fā)送操作的 case 語(yǔ)句被執(zhí)行,因?yàn)槿雲(yún)?nbsp;c 是一個(gè)有 1 個(gè)緩沖區(qū)大小的 channel,并且該 channel 中還沒(méi)有數(shù)據(jù),所以讀取操作的 case 語(yǔ)句沒(méi)有讀取到數(shù)據(jù),不滿(mǎn)足執(zhí)行條件。
有緩沖區(qū),已寫(xiě)滿(mǎn)數(shù)據(jù) channel
我們?cè)傩薷囊幌氯雲(yún)?nbsp;c,將入?yún)⒌?nbsp;c 改為 1 個(gè)緩沖區(qū)大小的 channel,并且寫(xiě)入字符串 Go。代碼如下:
func main() {
c := make(chan string, 1)
c <- "Go"
DoChannel(c)
}運(yùn)行代碼,讀執(zhí)行,寫(xiě)未執(zhí)行。
即 select 中的對(duì)入?yún)?nbsp;channel 進(jìn)行接收操作的 case 語(yǔ)句被執(zhí)行,因?yàn)槿雲(yún)?nbsp;c 是一個(gè)有 1 個(gè)緩沖區(qū)大小,并且已寫(xiě)滿(mǎn)數(shù)據(jù),所以讀取操作的 case 語(yǔ)句可以讀取到數(shù)據(jù),滿(mǎn)足執(zhí)行條件。
而寫(xiě)入操作的 case 無(wú)法寫(xiě)入數(shù)據(jù),不滿(mǎn)足執(zhí)行條件。
有緩沖區(qū),有數(shù)據(jù),可寫(xiě)數(shù)據(jù) channel
最后一種場(chǎng)景是既能讀取也能寫(xiě)入的 channel,我們修改一下入?yún)?nbsp;c,將入?yún)?nbsp;c 改為 2 個(gè)緩沖區(qū)大小的 channel,其中 1 個(gè)緩沖區(qū)寫(xiě)入字符串 Go,另外 1 個(gè)緩沖區(qū)還可以寫(xiě)入數(shù)據(jù)。代碼如下:
func main() {
c := make(chan string, 2)
c <- "Go"
DoChannel(c)
}通過(guò)多次運(yùn)行代碼,會(huì)發(fā)現(xiàn)讀取和寫(xiě)入的 case 語(yǔ)句都有機(jī)會(huì)執(zhí)行,因?yàn)閮蓚€(gè) case 語(yǔ)句都滿(mǎn)足執(zhí)行條件,但是只能有 1 個(gè) case 語(yǔ)句執(zhí)行,select 會(huì)隨機(jī)執(zhí)行其中 1 個(gè) case 語(yǔ)句。
至此,我們已經(jīng)介紹了 5 種 channel 在 select 中的運(yùn)行結(jié)果。
case 語(yǔ)句中聲明變量
上面的代碼中,我們發(fā)現(xiàn)在兩個(gè) case 語(yǔ)句中,讀操作我們將讀取到的數(shù)據(jù)賦值給變量 receive,實(shí)際上,我們也可以省略變量賦值操作。
如果我們需要將讀取到的數(shù)據(jù),賦值給變量的話(huà),一般建議將讀取 channel 返回的兩個(gè)值全部接收,其中一個(gè)是讀取到的數(shù)據(jù),另外一個(gè)是布爾值,代表 channel 中沒(méi)有數(shù)據(jù),并且已被關(guān)閉。代碼如下:
func main() {
c := make(chan string)
close(c)
DoChannelV2(c)
}
func DoChannelV2(c chan string) {
var (
receive string
ok bool
)
select {
case receive, ok = <-c:
if !ok {
fmt.Println("no data")
} else {
fmt.Println(receive)
}
}
}閱讀上面這段代碼,我們使用 close 將 c 關(guān)閉。select 中的讀操作 case 語(yǔ)句,可以通過(guò) ok 的值,得到 channel 中沒(méi)有數(shù)據(jù),且已被關(guān)閉,不必打印空數(shù)據(jù)。
03 總結(jié)
本文我們了解到 select 中的 case 語(yǔ)句可以讀取 channel,多個(gè) case 語(yǔ)句僅能其中 1 個(gè)被執(zhí)行。
每個(gè) case 語(yǔ)句僅能對(duì) 1 個(gè) channel 進(jìn)行讀寫(xiě)操作,如果讀操作未讀取到數(shù)據(jù)將陷入阻塞,如果寫(xiě)操作無(wú)法寫(xiě)入數(shù)據(jù)將陷入阻塞,如果所有 case 語(yǔ)句中的 channel 都陷入阻塞時(shí),select 也會(huì)陷入阻塞。
為了避免 select 陷入阻塞,我們可以使用 default 語(yǔ)句,需要注意的是,default 語(yǔ)句可以在 select 的任意位置,但是僅能包含 1 個(gè),而 case 語(yǔ)句可以包含多個(gè)。
網(wǎng)站題目:Go語(yǔ)言?xún)?nèi)置I/O多路復(fù)用機(jī)制
文章轉(zhuǎn)載:http://www.5511xx.com/article/dpoocjo.html


咨詢(xún)
建站咨詢(xún)
