为什么我的代码会死锁以及如何解决它

时间:2018-08-30 23:16:21

标签: go

为什么我的代码在从通道读取时会死锁(随后崩溃),我希望在完全读取通道后它会在读取时阻塞,但不会崩溃。我知道这是一个死锁状态,因为没有人正在写入通道并读取该通道上的块。

如何更改代码以读取所有频道内容,然后从main退出而不是崩溃。

去游乐场: https://play.golang.org/p/rjXZZOx1FFZ

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    news := make(chan int, 10)

    wg.Add(1)

    go foo(&wg, news) 

    wg.Wait()   

    for {
        fmt.Printf("reading: %v\n", <-news) 
        //crashes here after printing 0-9
    }

}

func foo(wg *sync.WaitGroup, news chan int) {
    for i:=0; i<10;i++ {
        fmt.Printf("Writing\n")
        news <- i
    }
    (*wg).Done()
}

3 个答案:

答案 0 :(得分:2)

程序死锁是因为通道上的主要块已接收并且没有其他goroutines将发送到该通道。

使用这种方法读取所有频道内容,然后从main退出:main读取频道直到关闭; foo写入所有值并关闭通道。

func main() {
    news := make(chan int, 10)
    go foo(news)
    // range breaks when the channel is closed
    for v := range news { 

        fmt.Printf("reading: %v\n", v)
    }

}

func foo(news chan int) {
    for i := 0; i < 10; i++ {
        fmt.Printf("Writing\n")
        news <- i
    }
    // close channel to indicate that no more values will be sent.
    close(news) 
}

Run it on the Playground

答案 1 :(得分:2)

为解释这种情况的发生原因,您从恐慌中得到的错误消息告诉您它的要旨:

fatal error: all goroutines are asleep - deadlock!

您无法让所有goroutine都在等待其他goroutine执行某项操作。在这种情况下,当运行foo的goroutine完成并且运行main的goroutine收到了发送到news通道的所有消息(0至9)后,您的程序剩下一个goroutine,等待在频道上接收消息,因为再也没有其他goroutine可以这样做,所以永远不会再有消息发送给它。

答案 2 :(得分:1)

代码由于死锁而崩溃。

fatal error: all goroutines are asleep - deadlock!

原始代码有两个有趣的问题。

如果您在foo goroutine中关闭新闻频道而不是死锁,您将永远在main中运行无限循环。

对2个简单的代码进行更改应该会使这一点更加清楚,并使其在处理news个项目之后退出。

https://play.golang.org/p/qSU7sV7Wrov

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    news := make(chan int, 10)

    wg.Add(1)

    go foo(&wg, news)

    wg.Wait()

    for n := range news {
        fmt.Printf("reading: %v\n", n)
    }
    fmt.Println("We're out of news!")
}

func foo(wg *sync.WaitGroup, news chan int) {
    for i := 0; i < 10; i++ {
        fmt.Printf("Writing\n")
        news <- i
    }
    close(news)
    wg.Done()
}

当然,您可以在不使用WaitGroups的情况下重写它,而只需使用其他人指出的通道,这可能是一个更优雅的解决方案。