淺談Go Channel 高級(jí)實(shí)踐
channel 是 golang 里相當(dāng)有趣的一個(gè)功能,在我使用 golang 編碼的經(jīng)驗(yàn)里,大部分事件都會(huì)是在享受 channel 和 goroutine 配合的樂(lè)趣。所以本文主要介紹 channel 的一些有趣的用法。
這里有 Oling Cat 翻譯的Go編程語(yǔ)言規(guī)范里關(guān)于 channel(信道)的描述:
信道提供了一種機(jī)制,它在兩個(gè)并發(fā)執(zhí)行的函數(shù)之間進(jìn)行同步,并通過(guò)傳遞(與該信道元素類(lèi)型相符的)值來(lái)進(jìn)行通信。
這個(gè)個(gè)描述又乏味、又枯燥。在我第一次閱讀的時(shí)候,完全不明白這到底是個(gè)什么玩意。事實(shí)上,可以認(rèn)為 channel 是一個(gè)管道或者先進(jìn)先出隊(duì)列,非常簡(jiǎn)單且輕量。channel 并不是 Golang 首創(chuàng)的。它同樣作為內(nèi)置功能出現(xiàn)在其他語(yǔ)言中。在大多數(shù)情況下,它是一個(gè)又大、又笨、又復(fù)雜的消息隊(duì)列系統(tǒng)的一個(gè)功能。
本文主要講實(shí)踐,原理部分會(huì)一筆帶過(guò),關(guān)于 go 語(yǔ)言并發(fā)實(shí)現(xiàn)和內(nèi)存模型后續(xù)會(huì)有文章。
channel 實(shí)現(xiàn)的源碼不復(fù)雜,推薦閱讀,https://github.com/golang/go/blob/master/src/runtime/chan.go
channel 是干什么的
意義:channel 是用來(lái)通信的
實(shí)際上:(數(shù)據(jù)拷貝了一份,并通過(guò) channel 傳遞,本質(zhì)就是個(gè)隊(duì)列)
channel 應(yīng)該用在什么地方
核心:需要通信的地方
例如以下場(chǎng)景:
- 通知廣播
- 交換數(shù)據(jù)
- 顯式同步
- 并發(fā)控制
- ...
記住!channel 不是用來(lái)實(shí)現(xiàn)鎖機(jī)制的,雖然有些地方可以用它來(lái)實(shí)現(xiàn)類(lèi)似讀寫(xiě)鎖,保護(hù)臨界區(qū)的功能,但不要這么用!
channel 用例實(shí)現(xiàn)
超時(shí)控制
// 利用 time.After 實(shí)現(xiàn) func main() { done := do() select { case <-done: // logic case <-time.After(3 * time.Second): // timeout } } func do() <-chan struct{} { done := make(chan struct{}) go func() { // do something // ... done <- struct{}{} }() return done }
取最快的結(jié)果
比較常見(jiàn)的一個(gè)場(chǎng)景是重試,第一個(gè)請(qǐng)求在指定超時(shí)時(shí)間內(nèi)沒(méi)有返回結(jié)果,這時(shí)重試第二次,取兩次中最快返回的結(jié)果使用。
超時(shí)控制在上面有,下面代碼部分就簡(jiǎn)單實(shí)現(xiàn)調(diào)用多次了。
func main() { ret := make(chan string, 3) for i := 0; i < cap(ret); i++ { go call(ret) } fmt.Println(<-ret) } func call(ret chan<- string) { // do something // ... ret <- "result" }
限制最大并發(fā)數(shù)
// 最大并發(fā)數(shù)為 2 limits := make(chan struct{}, 2) for i := 0; i < 10; i++ { go func() { // 緩沖區(qū)滿(mǎn)了就會(huì)阻塞在這 limits <- struct{}{} do() <-limits }() }
for...range 優(yōu)先
for ... range c { do } 這種寫(xiě)法相當(dāng)于 if _, ok := <-c; ok { do }
func main() { c := make(chan int, 20) go func() { for i := 0; i < 10; i++ { c <- i } close(c) }() // 當(dāng) c 被關(guān)閉后,取完里面的元素就會(huì)跳出循環(huán) for x := range c { fmt.Println(x) } }
多個(gè) goroutine 同步響應(yīng)
利用 close 廣播
func main() { c := make(chan struct{}) for i := 0; i < 5; i++ { go do(c) } close(c) } func do(c <-chan struct{}) { // 會(huì)阻塞直到收到 close <-c fmt.Println("hello") }
非阻塞的 select
select 本身是阻塞的,當(dāng)所有分支都不滿(mǎn)足就會(huì)一直阻塞,如果想不阻塞,那么一個(gè)什么都不干的 default 分支是最好的選擇
select { case <-done: return default: }
for{select{}} 終止
盡量不要用 break label 形式,而是把終止循環(huán)的條件放到 for 條件里來(lái)實(shí)現(xiàn)
for ok { select { case ch <- 0: case <-done: ok = false } }
channel 特性
基礎(chǔ)特性
操作 | 值為 nil 的 channel | 被關(guān)閉的 channel | 正常的 channel |
---|---|---|---|
close | panic | panic | 成功關(guān)閉 |
c<- | 永遠(yuǎn)阻塞 | panic | 阻塞或成功發(fā)送 |
<-c | 永遠(yuǎn)阻塞 | 永遠(yuǎn)不阻塞 | 阻塞或成功接收 |
happens-before 特性
- 無(wú)緩沖時(shí),接收 happens-before 發(fā)送
- 任何情況下,發(fā)送 happens-before 接收
- close happens-before 接收
參考
https://go101.org/article/channel.html
https://golang.org/doc/effective_go.html#channels
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Go語(yǔ)言strconv包實(shí)現(xiàn)字符串和數(shù)值類(lèi)型的相互轉(zhuǎn)換
這篇文章主要介紹了Go語(yǔ)言strconv包實(shí)現(xiàn)字符串和數(shù)值類(lèi)型的相互轉(zhuǎn)換,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03解決 Golang VS Code 插件下載安裝失敗的問(wèn)題
這篇文章主要介紹了解決 Golang VS Code 插件下載安裝失敗,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05Golang try catch與錯(cuò)誤處理的實(shí)現(xiàn)
社區(qū)不少人在談?wù)?nbsp;golang 為毛不用try/catch模式,而采用苛刻的recovery、panic、defer組合,本文就來(lái)詳細(xì)的介紹一下,感興趣的可以了解一下2021-07-07解決golang結(jié)構(gòu)體tag編譯錯(cuò)誤的問(wèn)題
這篇文章主要介紹了解決golang結(jié)構(gòu)體tag編譯錯(cuò)誤的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-05-05go語(yǔ)言yaml轉(zhuǎn)map、map遍歷的實(shí)現(xiàn)
本文主要介紹了go語(yǔ)言yaml轉(zhuǎn)map、map遍歷的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09詳解Golang語(yǔ)言HTTP客戶(hù)端實(shí)踐
本文主要介紹了Golang語(yǔ)言HTTP客戶(hù)端實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11