go語(yǔ)言中如何使用select的實(shí)現(xiàn)示例
在golang語(yǔ)言中,select語(yǔ)句 就是用來(lái)監(jiān)聽(tīng)和channel有關(guān)的IO操作,當(dāng)IO操作發(fā)生時(shí),觸發(fā)相應(yīng)的case動(dòng)作。
有了 select語(yǔ)句,可以實(shí)現(xiàn) main主線程 與 goroutine線程 之間的互動(dòng)。
1.基本語(yǔ)法
select { case <-ch1 : // 檢測(cè)有沒(méi)有數(shù)據(jù)可讀 // 一旦成功讀取到數(shù)據(jù),則進(jìn)行該case處理語(yǔ)句 case ch2 <- 1 : // 檢測(cè)有沒(méi)有數(shù)據(jù)可寫(xiě) // 一旦成功向ch2寫(xiě)入數(shù)據(jù),則進(jìn)行該case處理語(yǔ)句 default: // 如果以上都沒(méi)有符合條件,那么進(jìn)入default處理流程 }
注意事項(xiàng)
- select語(yǔ)句 只能用于channel信道的IO操作,每個(gè)case都必須是一個(gè)信道。
- 如果不設(shè)置 default條件,當(dāng)沒(méi)有IO操作發(fā)生時(shí),select語(yǔ)句就會(huì)一直阻塞;
- 如果有一個(gè)或多個(gè)IO操作發(fā)生時(shí),Go運(yùn)行時(shí)會(huì)隨機(jī)選擇一個(gè)case執(zhí)行,但此時(shí)將無(wú)法保證執(zhí)行順序;
- 對(duì)于case語(yǔ)句,如果存在信道值為nil的讀寫(xiě)操作,則該分支將被忽略,可以理解為相當(dāng)于從select語(yǔ)句中刪除了這個(gè)case;
- 對(duì)于空的 select語(yǔ)句,會(huì)引起死鎖;
- 對(duì)于在 for中的select語(yǔ)句,不能添加 default,否則會(huì)引起cpu占用過(guò)高的問(wèn)題;
(1)多個(gè)IO操作發(fā)生時(shí),case語(yǔ)句是隨機(jī)執(zhí)行的
func main() ?{ ?? ?ch1 := make(chan int, 1) ? // 創(chuàng)建 一個(gè)長(zhǎng)度帶緩沖的整型通道 ?? ?ch1 <- 1 ? ? ? ? ? ? ? ? ? // 向通道中寫(xiě)入數(shù)據(jù) ?? ?ch2 := make(chan int, 1) ?? ?ch2 <- 2 ?? ?select { ?? ? ? ?case <- ch1: ?? ? ? ??? ?fmt.Println("ch1 read") ?? ??? ?case <- ch2: ?? ??? ??? ?fmt.Println("ch2 read") ?? ?} }
多次執(zhí)行后,會(huì)隨機(jī)打印 “ch1 read” 或 “ch2 read”
(2)空select語(yǔ)句
func main() { select { } }
執(zhí)行后,引發(fā)死鎖,打印如下:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [select (no cases)]:
main.main()
xxx/test.go:4 +0x27
exit status 2
(3)for中的select 引起CPU資源消耗過(guò)高
func main() ?{ ?? ?quit := make(chan bool) ?? ?go func() { ?? ??? ?for { ?? ??? ??? ?select { ?? ??? ??? ?case <-quit: ?? ??? ??? ??? ?fmt.Println("quit") ?? ??? ??? ??? ?// 使用 return 就會(huì)退出整個(gè)goroutine線程;如果使用 break,程序仍然在for循環(huán)中執(zhí)行 ?? ??? ??? ??? ?return ?? ??? ??? ?default: ?? ??? ??? ??? ?fmt.Println("default") ?? ??? ??? ?} ?? ??? ?} ?? ?}() ?? ?time.Sleep(3 * time.Second) ?? ?quit <- true ? ? ? ? ?// 主線程在3秒后,向quit信道寫(xiě)入數(shù)據(jù) ?? ? ?? ?time.Sleep(2 * time.Second) ?? ?fmt.Println("main") }
在for{}的select語(yǔ)句中使用了 default后,線程就會(huì)無(wú)限執(zhí)行default條件,直到quit信道中讀到數(shù)據(jù),否則會(huì)一直在一個(gè)死循環(huán)中運(yùn)行,從而導(dǎo)致占滿(mǎn)整個(gè)CPU資源。
在 for{}的select語(yǔ)句中,不建議使用 default條件。
2.select語(yǔ)句的實(shí)際應(yīng)用
(1)實(shí)現(xiàn) main主線程與 goroutine線程 之間的交互、通信
// 通過(guò)控制臺(tái)輸入 "bye", 來(lái)控制main函數(shù)結(jié)束運(yùn)行 func main() ?{ ?? ?quit := make(chan bool) ?? ?ch := make(chan string) ?? ?go func() { ?? ??? ?for { ?? ??? ??? ?select { ?? ??? ??? ?case name := <-ch: ?? ??? ??? ??? ?fmt.Printf("from main msg: [%v]\n", name) ?? ??? ??? ??? ?if name == "bye" { ?? ??? ??? ??? ??? ?quit <- true ?? ??? ??? ??? ?} else { ?? ??? ??? ??? ??? ?quit <- false ?? ??? ??? ??? ?} ?? ??? ??? ?} ?? ??? ?} ?? ?}() ?? ?for { ?? ??? ?// 控制臺(tái)輸入 ?? ??? ?fmt.Print("please input string: ") ?? ??? ?scanner := bufio.NewScanner(os.Stdin) ?? ??? ?scanner.Scan() ?? ??? ?ch <- scanner.Text() ?? ??? ?isOver := <- quit ?? ??? ?if isOver { ?? ??? ??? ?break ?? ??? ?} ?? ?} ?? ?fmt.Println("main over") }
運(yùn)行:
from main msg: [aaa]
please input string: bbb
from main msg: [bbb]
please input string: bye
from main msg: [bye]
main over
(2)超時(shí)實(shí)現(xiàn)
func main() ?{ ?? ?quit := make(chan bool) ?? ?ch := make(chan int) ?? ?go func() { ?? ??? ?for { ?? ??? ??? ?select { ?? ??? ??? ?case num := <- ch: ?? ??? ??? ??? ?fmt.Println("num = ", num) ?? ??? ??? ?case <- time.After(5 * time.Second): ?? ??? ??? ??? ?fmt.Println("超時(shí)") ?? ??? ??? ??? ?quit <- true ?? ??? ??? ?} ?? ??? ?} ?? ?}() ?? ?for i := 0; i < 2; i++ { ?? ??? ?ch <- i ?? ??? ?time.Sleep(time.Second) ?? ?} ?? ?<- quit ? ? ? ? ? ? ? ? ? // 等待超時(shí)后, 結(jié)束 main主線程 ?? ?fmt.Println("程序結(jié)束") }
到此這篇關(guān)于go語(yǔ)言中如何使用select的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)go語(yǔ)言使用select內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go 實(shí)現(xiàn)百萬(wàn)WebSocket連接的方法示例
這篇文章主要介紹了Go 實(shí)現(xiàn)百萬(wàn)WebSocket連接的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08GoFrame?gmap遍歷hashmap?listmap?treemap使用技巧
這篇文章主要為大家介紹了GoFrame?gmap遍歷hashmap?listmap?treemap使用技巧的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06Go語(yǔ)言開(kāi)發(fā)保證并發(fā)安全實(shí)例詳解
這篇文章主要為大家介紹了Go語(yǔ)言開(kāi)發(fā)保證并發(fā)安全實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09Go語(yǔ)言工程實(shí)踐單元測(cè)試基準(zhǔn)測(cè)試示例詳解
這篇文章主要為大家介紹了Go語(yǔ)言工程實(shí)踐單元測(cè)試基準(zhǔn)測(cè)試示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02Skywalking-go自動(dòng)監(jiān)控增強(qiáng)使用探究
這篇文章主要介紹了Skywalking-go自動(dòng)監(jiān)控增強(qiáng)使用深入探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01淺談Golang?Slice切片如何擴(kuò)容的實(shí)現(xiàn)
本文主要介紹了淺談Golang?Slice切片如何擴(kuò)容的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02GoFrame?gtree樹(shù)形結(jié)構(gòu)的使用技巧示例
這篇文章主要為大家介紹了GoFrame?gtree樹(shù)形結(jié)構(gòu)的使用技巧示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06基于Go語(yǔ)言搭建靜態(tài)文件服務(wù)器的詳細(xì)教程
Go 是一個(gè)開(kāi)源的編程語(yǔ)言,它能讓構(gòu)造簡(jiǎn)單、可靠且高效的軟件變得容易,本文給大家介紹了基于Go語(yǔ)言搭建靜態(tài)文件服務(wù)器的詳細(xì)教程,文中通過(guò)圖文和代碼講解的非常詳細(xì),需要的朋友可以參考下2024-10-10