亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

去通道死鎖問題

去通道死鎖問題

Go
收到一只叮咚 2023-05-04 17:30:48
剛開始學習golang,并沒有完全理解死鎖是怎么產生的。這是一個改編自 golang 游樂場教程的示例: package mainimport "fmt"func fibonacci(c, quit chan int) {    x, y := 0, 1    for {        select {        case c <- x:            x, y = y, x+y        case q:= <-quit:            fmt.Println(q)            return        }    }}func pp(c chan int, quit chan int){   for i := 0; i < 10; i++ {            fmt.Println(<-c)        }        quit <- 0}func main() {    c := make(chan int)    quit := make(chan int)   // here it's good, no deadlock     go pp(c,quit)         fibonacci(c, quit)   // but if change the order of above two line:   // fibonacci(c,quit)   // go pp(c,quit)   // it will deadlock}為什么上面兩行的順序很重要?
查看完整描述

3 回答

?
江戶川亂折騰

TA貢獻1851條經驗 獲得超5個贊

您有兩個功能,它們需要同時運行才能使通道通信正常工作 - 一個必須同時接收另一個發送。在這種情況下:

?go?pp(c,quit)????
?fibonacci(c,?quit)

pp從一個 goroutine 開始,它開始運行,然后你調用fibonacci,這樣兩者都在運行,一切正常。如果按照您的建議將其更改為:

?fibonacci(c,?quit)?
?go?pp(c,quit)

然后你作為一個常規函數調用,而不是作為一個 goroutine,這意味著下一行在返回fibonacci之前不會被執行。fibonacci因為fibonacci期望從它的通道接收到一些東西,所以它會阻塞直到那發生——這是永遠不會發生的,因為沒有任何東西同時從它讀取。因此你的僵局。

問題不在于函數的順序或通道緩沖——問題在于如果你想同時運行兩個函數,你首先調用的那個必須作為 goroutine 運行(或兩者):

?go?fibonacci(c,?quit)
?pp(c,quit)

可以正常工作,因為它fibonacci同時調用,然后pp可以同時運行的調用。

如果您使用的是 a?WaitGroup,您甚至可以將它們都作為 goroutines 運行,并且它們會同時運行:

?go?fibonacci(c,?quit,?wg)
??go?pp(c,quit,?wg)

盡管在您的情況下這不是必需的并且增加了復雜性。


查看完整回答
反對 回復 2023-05-04
?
千萬里不及你

TA貢獻1784條經驗 獲得超9個贊

通道的make(chan int)隱式大小為零(

大小為零的通道是無緩沖的。make(chan int, n)緩沖指定大小的通道。

在這里,c := make(chan int)是無緩沖的。

如果改變這兩行的順序

?go?pp(c,quit)????
?fibonacci(c,?quit)

fibonacci(c,quit)go?pp(c,quit)

它會導致程序死鎖。在fibonacci函數中,看select語句。

select?{????case?c?<-?x:
????????x,?y?=?y,?x+y?
???????????case?q:=?<-quit:
????????fmt.Println(q)?
???????????????return
???????????????}

selectstatement 將保持阻塞狀態,直到其中一個case被 fullfilled。由于go pp(c,quit)在 之后執行fibonacci(c,quit),因此沒有清除通道c或向通道發送信號的過程quit。這就是函數fibonacci(c,quit)將保持阻塞的原因。


查看完整回答
反對 回復 2023-05-04
?
慕妹3146593

TA貢獻1820條經驗 獲得超9個贊

如果您先調用 fibonnaci,它會在通道上發送值,但接收器尚未準備好。這就是僵局背后的原因。


筆記:


默認情況下,發送和接收阻塞,直到另一方準備就緒。這允許 goroutines 在沒有顯式鎖或條件變量的情況下進行同步。


如果您想更改程序的順序,看看我們如何避免死鎖。


package main


import "fmt"


func fibonacci(c, quit chan int) {

    x, y := 0, 1

    for {

        select {

        case c <- x:

            x, y = y, x+y

        case q:= <-quit:

            return

        }

    }

}


func pp(c chan int, quit chan int){

   for i := 0; i < 10; i++ {

            fmt.Println(<-c)

        }

        quit <- 0

}


func main() {

    c := make(chan int)

    quit := make(chan int)


    go func(){

       fibonacci(c, quit)

    }()

    pp(c,quit)

}

Go 操場上的工作代碼


在這種情況下,永遠記得等待 go routine 完成。但是當你首先調用 fibonnaci 時它已經發送了值但是接收者還沒有準備好導致死鎖。


編輯:


因為即使你等待 go routine 完成。它仍然會造成死鎖,因為通道不同步為:


包主


import (

    "fmt"

    "sync"

)


var wg sync.WaitGroup


func fibonacci(c, quit chan int) {

    x, y := 0, 1

    for {

        select {

        case c <- x:

            x, y = y, x+y

        case q:= <-quit:

            fmt.Println(q)

            return

        }

    }

}


func pp(c chan int, quit chan int){

   defer wg.Done()

   for i := 0; i < 10; i++ {

            fmt.Println(<-c)

   }

   quit <- 0

}


func main() {

    c := make(chan int)

    quit := make(chan int)

    fibonacci(c, quit)

    wg.Add(1)

    go pp(c,quit)  

    wg.Wait()

}

輸出:


致命錯誤:所有 goroutines 都睡著了——死鎖!


goroutine 1 [選擇]: main.fibonacci(0x434080, 0x4340c0) /tmp/sandbox779301309/main.go:13 +0xc0 main.main() /tmp/sandbox779301309/main.go:34 +0x80


如果您更改代碼并在 for 循環的選擇中創建默認情況。然后它將滿足這種情況并返回,而您的 main 將退出。永無止境的循環讓它在退出的情況下等待返回以使其返回。這將起作用:


package main


import (

    "fmt"

    "sync"

)


var wg sync.WaitGroup


func fibonacci(c, quit chan int) {

    x, y := 0, 1

    for {

        select {

        case c <- x:

            x, y = y, x+y

        case q, ok := <-quit:

            if ok {

                fmt.Println(q)

            }

            return

        default:

            fmt.Println("No value in any of the channel")

            return

        }

    }

}


func pp(c chan int, quit chan int) {

    for i := 0; i < 10; i++ {

        if value, ok := <-c; ok {

            fmt.Println(value)

        }

    }

    quit <- 0

}


func main() {

    c := make(chan int)

    quit := make(chan int)

    fibonacci(c, quit)

    go pp(c, quit)

}


查看完整回答
反對 回復 2023-05-04
  • 3 回答
  • 0 關注
  • 187 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號