国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Golang > 正文

淺談Go Channel 高級實踐

2020-04-01 18:53:42
字體:
來源:轉載
供稿:網友

channel 是 golang 里相當有趣的一個功能,在我使用 golang 編碼的經驗里,大部分事件都會是在享受 channel 和 goroutine 配合的樂趣。所以本文主要介紹 channel 的一些有趣的用法。

這里有 Oling Cat 翻譯的Go編程語言規范里關于 channel(信道)的描述:

信道提供了一種機制,它在兩個并發執行的函數之間進行同步,并通過傳遞(與該信道元素類型相符的)值來進行通信。

這個個描述又乏味、又枯燥。在我第一次閱讀的時候,完全不明白這到底是個什么玩意。事實上,可以認為 channel 是一個管道或者先進先出隊列,非常簡單且輕量。channel 并不是 Golang 首創的。它同樣作為內置功能出現在其他語言中。在大多數情況下,它是一個又大、又笨、又復雜的消息隊列系統的一個功能。

本文主要講實踐,原理部分會一筆帶過,關于 go 語言并發實現和內存模型后續會有文章。

channel 實現的源碼不復雜,推薦閱讀,https://github.com/golang/go/blob/master/src/runtime/chan.go

channel 是干什么的

意義:channel 是用來通信的

實際上:(數據拷貝了一份,并通過 channel 傳遞,本質就是個隊列)

channel 應該用在什么地方

核心:需要通信的地方

例如以下場景:

  • 通知廣播
  • 交換數據
  • 顯式同步
  • 并發控制
  • ...

記??!channel 不是用來實現鎖機制的,雖然有些地方可以用它來實現類似讀寫鎖,保護臨界區的功能,但不要這么用!

channel 用例實現

超時控制

// 利用 time.After 實現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}

取最快的結果

比較常見的一個場景是重試,第一個請求在指定超時時間內沒有返回結果,這時重試第二次,取兩次中最快返回的結果使用。
超時控制在上面有,下面代碼部分就簡單實現調用多次了。

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"}

限制最大并發數

// 最大并發數為 2limits := make(chan struct{}, 2)for i := 0; i < 10; i++ {  go func() {    // 緩沖區滿了就會阻塞在這    limits <- struct{}{}    do()    <-limits  }()}

for...range 優先

for ... range c { do } 這種寫法相當于 if _, ok := <-c; ok { do }

func main() {  c := make(chan int, 20)  go func() {    for i := 0; i < 10; i++ {      c <- i    }    close(c)  }()  // 當 c 被關閉后,取完里面的元素就會跳出循環  for x := range c {    fmt.Println(x)  }}

多個 goroutine 同步響應

利用 close 廣播

func main() {  c := make(chan struct{})  for i := 0; i < 5; i++ {    go do(c)  }  close(c)}func do(c <-chan struct{}) {  // 會阻塞直到收到 close  <-c  fmt.Println("hello")}

非阻塞的 select

select 本身是阻塞的,當所有分支都不滿足就會一直阻塞,如果想不阻塞,那么一個什么都不干的 default 分支是最好的選擇

select {case <-done:  returndefault:  }

for{select{}} 終止

盡量不要用 break label 形式,而是把終止循環的條件放到 for 條件里來實現

for ok {  select {  case ch <- 0:  case <-done:    ok = false  }}

channel 特性

基礎特性

 

操作 值為 nil 的 channel 被關閉的 channel 正常的 channel
close panic panic 成功關閉
c<- 永遠阻塞 panic 阻塞或成功發送
<-c 永遠阻塞 永遠不阻塞 阻塞或成功接收

 

happens-before 特性

  1. 無緩沖時,接收 happens-before 發送
  2. 任何情況下,發送 happens-before 接收
  3. close happens-before 接收

參考

https://go101.org/article/channel.html
https://golang.org/doc/effective_go.html#channels

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 娱乐| 威宁| 宿松县| 寻甸| 湖北省| 阳信县| 肥西县| 北宁市| 东丰县| 常州市| 仁化县| 南溪县| 岳普湖县| 武穴市| 舒城县| 容城县| 威信县| 嵩明县| 昌邑市| 醴陵市| 东辽县| 福贡县| 衡南县| 柏乡县| 白河县| 保定市| 新化县| 乌海市| 罗田县| 韶关市| 遂平县| 泰和县| 崇仁县| 田林县| 朝阳区| 体育| 闸北区| 凤翔县| 平度市| 永城市| 台江县|