成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Go select 竟然死鎖了。。。

開發(fā) 后端
按常規(guī)理解,go func 中的 select 應該執(zhí)行 default 分支,程序正常運行。但結果卻不是,而是死鎖。可以通過該鏈接測試:https://play.studygolang.com/p/kF4pOjYXbXf。

[[420983]]

大家好,我是 polarisxu。

前兩天,火丁筆記發(fā)了一篇文章:《一個 select 死鎖問題》[1],又是一個小細節(jié)。我將其中的問題改一下,更好理解:

  1. package main 
  2.  
  3. import "sync" 
  4.  
  5. func main() { 
  6.  var wg sync.WaitGroup 
  7.  foo := make(chan int
  8.  bar := make(chan int
  9.  wg.Add(1) 
  10.  go func() { 
  11.   defer wg.Done() 
  12.   select { 
  13.   case foo <- <-bar: 
  14.   default
  15.    println("default"
  16.   } 
  17.  }() 
  18.  wg.Wait() 

按常規(guī)理解,go func 中的 select 應該執(zhí)行 default 分支,程序正常運行。但結果卻不是,而是死鎖。可以通過該鏈接測試:https://play.studygolang.com/p/kF4pOjYXbXf。

原因文章也解釋了,Go 語言規(guī)范中有這么一句:

For all the cases in the statement, the channel operands of receive operations and the channel and right-hand-side expressions of send statements are evaluated exactly once, in source order, upon entering the “select” statement. The result is a set of channels to receive from or send to, and the corresponding values to send. Any side effects in that evaluation will occur irrespective of which (if any) communication operation is selected to proceed. Expressions on the left-hand side of a RecvStmt with a short variable declaration or assignment are not yet evaluated.

不知道大家看懂沒有?于是,最后來了一個例子驗證你是否理解了:為什么每次都是輸出一半數(shù)據(jù),然后死鎖?(同樣,這里可以運行查看結果:https://play.studygolang.com/p/zoJtTzI7K5T)

  1. package main 
  2.  
  3. import ( 
  4.  "fmt" 
  5.  "time" 
  6.  
  7. func talk(msg string, sleep int) <-chan string { 
  8.  ch := make(chan string) 
  9.  go func() { 
  10.   for i := 0; i < 5; i++ { 
  11.    ch <- fmt.Sprintf("%s %d", msg, i) 
  12.    time.Sleep(time.Duration(sleep) * time.Millisecond) 
  13.   } 
  14.  }() 
  15.  return ch 
  16.  
  17. func fanIn(input1, input2 <-chan string) <-chan string { 
  18.  ch := make(chan string) 
  19.  go func() { 
  20.   for { 
  21.    select { 
  22.    case ch <- <-input1: 
  23.    case ch <- <-input2: 
  24.    } 
  25.   } 
  26.  }() 
  27.  return ch 
  28.  
  29. func main() { 
  30.  ch := fanIn(talk("A", 10), talk("B", 1000)) 
  31.  for i := 0; i < 10; i++ { 
  32.   fmt.Printf("%q\n", <-ch) 
  33.  } 

有沒有這種感覺:

算法入門

這是 StackOverflow 上的一個問題:https://stackoverflow.com/questions/51167940/chained-channel-operations-in-a-single-select-case。

關鍵點和文章開頭例子一樣,在于 select case 中兩個 channel 串起來,即 fanIn 函數(shù)中:

  1. select { 
  2. case ch <- <-input1: 
  3. case ch <- <-input2: 

如果改為這樣就一切正常:

  1. select { 
  2. case t := <-input1: 
  3.   ch <- t 
  4. case t := <-input2: 
  5.   ch <- t 

結合這個更復雜的例子分析 Go 語言規(guī)范中的那句話。

對于 select 語句,在進入該語句時,會按源碼的順序對每一個 case 子句進行求值:這個求值只針對發(fā)送或接收操作的額外表達式。

比如:

  1. // ch 是一個 chan int; 
  2. // getVal() 返回 int 
  3. // input 是 chan int 
  4. // getch() 返回 chan int 
  5. select { 
  6.   case ch <- getVal(): 
  7.   case ch <- <-input: 
  8.   case getch() <- 1: 
  9.   case <- getch(): 

在沒有選擇某個具體 case 執(zhí)行前,例子中的 getVal()、<-input 和 getch() 會執(zhí)行。這里有一個驗證的例子:https://play.studygolang.com/p/DkpCq3aQ1TE。

  1. package main 
  2.  
  3. import ( 
  4.  "fmt" 
  5.  
  6. func main() { 
  7.  ch := make(chan int
  8.  go func() { 
  9.   select { 
  10.   case ch <- getVal(1): 
  11.    fmt.Println("in first case"
  12.   case ch <- getVal(2): 
  13.    fmt.Println("in second case"
  14.   default
  15.    fmt.Println("default"
  16.   } 
  17.  }() 
  18.  
  19.  fmt.Println("The val:", <-ch) 
  20.  
  21. func getVal(i intint { 
  22.  fmt.Println("getVal, i=", i) 
  23.  return i 

無論 select 最終選擇了哪個 case,getVal() 都會按照源碼順序執(zhí)行:getVal(1) 和 getVal(2),也就是它們必然先輸出:

  1. getVal, i= 1 
  2. getVal, i= 2 

你可以仔細琢磨一下。

現(xiàn)在回到 StackOverflow 上的那個問題。

每次進入以下 select 語句時:

  1. select { 
  2. case ch <- <-input1: 
  3. case ch <- <-input2: 

<-input1 和 <-input2 都會執(zhí)行,相應的值是:A x 和 B x(其中 x 是 0-5)。但每次 select 只會選擇其中一個 case 執(zhí)行,所以 <-input1 和 <-input2 的結果,必然有一個被丟棄了,也就是不會被寫入 ch 中。因此,一共只會輸出 5 次,另外 5 次結果丟掉了。(你會發(fā)現(xiàn),輸出的 5 次結果中,x 比如是 0 1 2 3 4)

而 main 中循環(huán) 10 次,只獲得 5 次結果,所以輸出 5 次后,報死鎖。

雖然這是一個小細節(jié),但實際開發(fā)中還是有可能出現(xiàn)的。比如文章提到的例子寫法:

  1. // ch 是一個 chan int; 
  2. // getVal() 返回 int 
  3. // input 是 chan int 
  4. // getch() 返回 chan int 
  5. select { 
  6.   case ch <- getVal(): 
  7.   case ch <- <-input: 
  8.   case getch() <- 1: 
  9.   case <- getch(): 

因此在使用 select 時,一定要注意這種可能的問題。

不要以為這個問題不會遇到,其實很常見。最多的就是 time.After 導致內(nèi)存泄露問題,網(wǎng)上有很多文章解釋原因,如何避免,其實最根本原因就是因為 select 這個機制導致的。

比如如下代碼,有內(nèi)存泄露(傳遞給 time.After 的時間參數(shù)越大,泄露會越厲害),你能解釋原因嗎?

  1. package main 
  2.  
  3. import ( 
  4.     "time" 
  5.  
  6. func main()  { 
  7.     ch := make(chan int, 10) 
  8.  
  9.     go func() { 
  10.         var i = 1 
  11.         for { 
  12.             i++ 
  13.             ch <- i 
  14.         } 
  15.     }() 
  16.  
  17.     for { 
  18.         select { 
  19.         case x := <- ch: 
  20.             println(x) 
  21.         case <- time.After(30 * time.Second): 
  22.             println(time.Now().Unix()) 
  23.         } 
  24.     } 

參考資料

[1]《一個 select 死鎖問題》: https://blog.huoding.com/2021/08/29/947

本文轉載自微信公眾號「polarisxu」,可以通過以下二維碼關注。轉載本文請聯(lián)系polarisxu公眾號。

 

責任編輯:武曉燕 來源: polarisxu
相關推薦

2022-07-05 11:48:47

MySQL死鎖表鎖

2023-07-19 08:01:04

switch?select?語句

2020-10-16 09:09:56

代碼業(yè)務模型

2023-10-25 08:21:15

悲觀鎖MySQL

2023-11-15 14:34:05

MySQL悲觀鎖

2019-01-29 10:00:59

GitHub開源搜索

2023-03-09 09:06:13

ChanneGo開發(fā)

2017-11-02 15:44:11

內(nèi)存降價價格

2023-09-15 11:32:18

selectGo可視化解釋

2022-09-15 14:04:07

Go語言泛型

2020-07-08 13:38:21

Go拼音工具

2025-02-13 07:49:18

2024-01-04 08:12:12

IDE代碼出錯ChatGPT

2019-10-28 08:44:29

Code Review代碼團隊

2023-03-10 07:46:55

Go開發(fā)Channelselect

2023-12-06 07:16:31

Go語言語句

2022-02-22 08:55:29

SelectPoll/ Epoll

2021-04-16 20:47:42

Go 指令函數(shù)

2021-08-27 09:45:15

flink stateJava enum 踩坑記

2017-07-12 09:46:00

5G社會網(wǎng)絡
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 爱操av| 欧美在线激情 | 午夜影院免费体验区 | 国产1区 | 国产精品成av人在线视午夜片 | 国产男女猛烈无遮掩视频免费网站 | 日本欧美在线 | 羞羞的视频免费在线观看 | 成人免费看黄网站在线观看 | 日韩欧美中文字幕在线视频 | 日韩精品在线播放 | 久久久久久久久国产成人免费 | av中文在线观看 | 久久综合狠狠综合久久综合88 | 午夜爽爽爽男女免费观看 | 在线播放国产视频 | 91久久精品国产91久久 | 色综久久 | 久久久国产一区二区三区四区小说 | 国产一区二区精品在线观看 | 精品9999| 日本亚洲一区二区 | 国产一区二区三区高清 | 欧美日韩国产欧美 | 国产日韩一区二区三区 | 国产精品久久国产精品久久 | 黄色国产 | 国产高清一区二区 | 日韩欧美中文 | 日韩看片 | 日韩一二区 | 日韩一二三区视频 | 99re在线 | 免费在线观看av片 | 精品亚洲一区二区 | 日韩精品无码一区二区三区 | 视频一区二区在线观看 | 欧美女优在线观看 | 亚洲成av片人久久久 | 欧美激情视频网站 | 亚洲高清一区二区三区 |