欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Go select 死鎖的一個(gè)細(xì)節(jié)

 更新時(shí)間:2021年10月08日 15:02:16   作者:polarisxu  
這篇文章主要給大家分享的是Go select 死鎖的一個(gè)細(xì)節(jié),文章先是對(duì)主題提出問題,然后展開內(nèi)容,感興趣的小伙伴可以借鑒一下,希望對(duì)你有所幫助

下面對(duì)是一個(gè) select 死鎖的問題

package main

import "sync"

func main() {
 var wg sync.WaitGroup
 foo := make(chan int)
 bar := make(chan int)
 wg.Add(1)
 go func() {
  defer wg.Done()
  select {
  case foo <- <-bar:
  default:
   println("default")
  }
 }()
 wg.Wait()
}

按常規(guī)理解,go func 中的 select 應(yīng)該執(zhí)行 default 分支,程序正常運(yùn)行。但結(jié)果卻不是,而是死鎖??梢酝ㄟ^該鏈接測(cè)試: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.

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

package main

import (
 "fmt"
 "time"
)

func talk(msg string, sleep int) <-chan string {
 ch := make(chan string)
 go func() {
  for i := 0; i < 5; i++ {
   ch <- fmt.Sprintf("%s %d", msg, i)
   time.Sleep(time.Duration(sleep) * time.Millisecond)
  }
 }()
 return ch
}

func fanIn(input1, input2 <-chan string) <-chan string {
 ch := make(chan string)
 go func() {
  for {
   select {
   case ch <- <-input1:
   case ch <- <-input2:
   }
  }
 }()
 return ch
}

func main() {
 ch := fanIn(talk("A", 10), talk("B", 1000))
 for i := 0; i < 10; i++ {
  fmt.Printf("%q\n", <-ch)
 }
}

有沒有這種感覺:

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

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

select {
case ch <- <-input1:
case ch <- <-input2:
}


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

select {
case t := <-input1:
  ch <- t
case t := <-input2:
  ch <- t
}

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

對(duì)于 select 語句,在進(jìn)入該語句時(shí),會(huì)按源碼的順序?qū)γ恳粋€(gè) case 子句進(jìn)行求值:這個(gè)求值只針對(duì)發(fā)送或接收操作的額外表達(dá)式。

比如:

// ch 是一個(gè) chan int;
// getVal() 返回 int
// input 是 chan int
// getch() 返回 chan int
select {
  case ch <- getVal():
  case ch <- <-input:
  case getch() <- 1:
  case <- getch():
}

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

package main

import (
 "fmt"
)

func main() {
 ch := make(chan int)
 go func() {
  select {
  case ch <- getVal(1):
   fmt.Println("in first case")
  case ch <- getVal(2):
   fmt.Println("in second case")
  default:
   fmt.Println("default")
  }
 }()

 fmt.Println("The val:", <-ch)
}

func getVal(i int) int {
 fmt.Println("getVal, i=", i)
 return i
}

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

getVal, i= 1
getVal, i= 2

你可以仔細(xì)琢磨一下。

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

每次進(jìn)入以下 select 語句時(shí):

select {
case ch <- <-input1:
case ch <- <-input2:
}


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

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

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

// ch 是一個(gè) chan int;
// getVal() 返回 int
// input 是 chan int
// getch() 返回 chan int
select {
  case ch <- getVal():
  case ch <- <-input:
  case getch() <- 1:
  case <- getch():
}

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

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

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

package main

import (
    "time"
)

func main()  {
    ch := make(chan int, 10)

    go func() {
        var i = 1
        for {
            i++
            ch <- i
        }
    }()

    for {
        select {
        case x := <- ch:
            println(x)
        case <- time.After(30 * time.Second):
            println(time.Now().Unix())
        }
    }
}

到此這篇關(guān)于Go select 死鎖的一個(gè)細(xì)節(jié)的文章就介紹到這了,更多相關(guān)Go select 死鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • go開源Hugo站點(diǎn)渲染之模板詞法解析

    go開源Hugo站點(diǎn)渲染之模板詞法解析

    這篇文章主要為大家介紹了go開源Hugo站點(diǎn)渲染之模板詞法解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • golang基于errgroup實(shí)現(xiàn)并發(fā)調(diào)用的方法

    golang基于errgroup實(shí)現(xiàn)并發(fā)調(diào)用的方法

    這篇文章主要介紹了golang基于errgroup實(shí)現(xiàn)并發(fā)調(diào)用,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-09-09
  • golang 如何獲取map所有key的方式

    golang 如何獲取map所有key的方式

    這篇文章主要介紹了golang 獲取map所有key的方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Go語言實(shí)現(xiàn)MapReduce的示例代碼

    Go語言實(shí)現(xiàn)MapReduce的示例代碼

    MapReduce是一種備受歡迎的編程模型,它最初由Google開發(fā),用于并行處理大規(guī)模數(shù)據(jù)以提取有價(jià)值的信息,本文將使用GO語言實(shí)現(xiàn)一個(gè)簡(jiǎn)單的MapReduce,需要的可以參考下
    2023-10-10
  • 一文詳解go中如何實(shí)現(xiàn)定時(shí)任務(wù)

    一文詳解go中如何實(shí)現(xiàn)定時(shí)任務(wù)

    定時(shí)任務(wù)是指按照預(yù)定的時(shí)間間隔或特定時(shí)間點(diǎn)自動(dòng)執(zhí)行的計(jì)劃任務(wù)或操作,這篇文章主要為大家詳細(xì)介紹了go中是如何實(shí)現(xiàn)定時(shí)任務(wù)的,感興趣的可以了解下
    2023-11-11
  • Go存儲(chǔ)基礎(chǔ)使用direct io方法實(shí)例

    Go存儲(chǔ)基礎(chǔ)使用direct io方法實(shí)例

    這篇文章主要介紹了Go存儲(chǔ)基礎(chǔ)之如何使用direct io方法實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • GoFrame框架Scan類型轉(zhuǎn)換實(shí)例

    GoFrame框架Scan類型轉(zhuǎn)換實(shí)例

    這篇文章主要為大家介紹了GoFrame框架Scan類型轉(zhuǎn)換的實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Go簡(jiǎn)單實(shí)現(xiàn)協(xié)程池的實(shí)現(xiàn)示例

    Go簡(jiǎn)單實(shí)現(xiàn)協(xié)程池的實(shí)現(xiàn)示例

    本文主要介紹了Go簡(jiǎn)單實(shí)現(xiàn)協(xié)程池的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • Go語言實(shí)現(xiàn)的web爬蟲實(shí)例

    Go語言實(shí)現(xiàn)的web爬蟲實(shí)例

    這篇文章主要介紹了Go語言實(shí)現(xiàn)的web爬蟲,實(shí)例分析了web爬蟲的原理與Go語言的實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • Go語言實(shí)現(xiàn)冒泡排序、選擇排序、快速排序及插入排序的方法

    Go語言實(shí)現(xiàn)冒泡排序、選擇排序、快速排序及插入排序的方法

    這篇文章主要介紹了Go語言實(shí)現(xiàn)冒泡排序、選擇排序、快速排序及插入排序的方法,以實(shí)例形式詳細(xì)分析了幾種常見的排序技巧與實(shí)現(xiàn)方法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-02-02

最新評(píng)論