猿问

去通道死锁问题

刚开始学习golang,并没有完全理解死锁是怎么产生的。这是一个改编自 golang 游乐场教程的示例:


 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:

            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

}

为什么上面两行的顺序很重要?


收到一只叮咚
浏览 133回答 3
3回答

江户川乱折腾

您有两个功能,它们需要同时运行才能使通道通信正常工作 - 一个必须同时接收另一个发送。在这种情况下: 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)尽管在您的情况下这不是必需的并且增加了复杂性。

千万里不及你

通道的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)将保持阻塞的原因。

慕妹3146593

如果您先调用 fibonnaci,它会在通道上发送值,但接收器尚未准备好。这就是僵局背后的原因。笔记:默认情况下,发送和接收阻塞,直到另一方准备就绪。这允许 goroutines 在没有显式锁或条件变量的情况下进行同步。如果您想更改程序的顺序,看看我们如何避免死锁。package mainimport "fmt"func fibonacci(c, quit chan int) {&nbsp; &nbsp; x, y := 0, 1&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; select {&nbsp; &nbsp; &nbsp; &nbsp; case c <- x:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; x, y = y, x+y&nbsp; &nbsp; &nbsp; &nbsp; case q:= <-quit:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}func pp(c chan int, quit chan int){&nbsp; &nbsp;for i := 0; i < 10; i++ {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(<-c)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; quit <- 0}func main() {&nbsp; &nbsp; c := make(chan int)&nbsp; &nbsp; quit := make(chan int)&nbsp; &nbsp; go func(){&nbsp; &nbsp; &nbsp; &nbsp;fibonacci(c, quit)&nbsp; &nbsp; }()&nbsp; &nbsp; pp(c,quit)}Go 操场上的工作代码在这种情况下,永远记得等待 go routine 完成。但是当你首先调用 fibonnaci 时它已经发送了值但是接收者还没有准备好导致死锁。编辑:因为即使你等待 go routine 完成。它仍然会造成死锁,因为通道不同步为:包主import (&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "sync")var wg sync.WaitGroupfunc fibonacci(c, quit chan int) {&nbsp; &nbsp; x, y := 0, 1&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; select {&nbsp; &nbsp; &nbsp; &nbsp; case c <- x:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; x, y = y, x+y&nbsp; &nbsp; &nbsp; &nbsp; case q:= <-quit:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(q)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}func pp(c chan int, quit chan int){&nbsp; &nbsp;defer wg.Done()&nbsp; &nbsp;for i := 0; i < 10; i++ {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(<-c)&nbsp; &nbsp;}&nbsp; &nbsp;quit <- 0}func main() {&nbsp; &nbsp; c := make(chan int)&nbsp; &nbsp; quit := make(chan int)&nbsp; &nbsp; fibonacci(c, quit)&nbsp; &nbsp; wg.Add(1)&nbsp; &nbsp; go pp(c,quit)&nbsp;&nbsp;&nbsp; &nbsp; 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 mainimport (&nbsp; &nbsp; "fmt"&nbsp; &nbsp; "sync")var wg sync.WaitGroupfunc fibonacci(c, quit chan int) {&nbsp; &nbsp; x, y := 0, 1&nbsp; &nbsp; for {&nbsp; &nbsp; &nbsp; &nbsp; select {&nbsp; &nbsp; &nbsp; &nbsp; case c <- x:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; x, y = y, x+y&nbsp; &nbsp; &nbsp; &nbsp; case q, ok := <-quit:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if ok {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(q)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; &nbsp; &nbsp; default:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println("No value in any of the channel")&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}func pp(c chan int, quit chan int) {&nbsp; &nbsp; for i := 0; i < 10; i++ {&nbsp; &nbsp; &nbsp; &nbsp; if value, ok := <-c; ok {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(value)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; quit <- 0}func main() {&nbsp; &nbsp; c := make(chan int)&nbsp; &nbsp; quit := make(chan int)&nbsp; &nbsp; fibonacci(c, quit)&nbsp; &nbsp; go pp(c, quit)}
随时随地看视频慕课网APP

相关分类

Go
我要回答