在这种情况下没有出现恐慌

时间:2018-07-26 21:40:17

标签: go

我看到了恐慌

func main() {
    ch := make(chan int)
    ch <- 1
    fmt.Println(<-ch)
}

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
    /tmp/sandbox058504389/main.go:10 +0x60

我相信,go会检测到主例程在写入时被阻塞,并且由于在写入非缓冲通道时发生了阻塞,因此永远不会到达读取代码段,并且我们会感到恐慌。为什么我们在下面的代码中看不到问题,为什么不慌张

func main() {
    fmt.Println("Hello, playground")

    var wg sync.WaitGroup

    ch := make(chan int)

    wg.Add(1)
    go func() {
        <-ch 
        wg.Done()
    }()

    wg.Wait()
}

如果在有效的代码段中,如果我更改<-ch,即将通道读取更改为ch<-,这是写操作,那么我确实会看到错误。有人可以解释一下为什么吗?

1 个答案:

答案 0 :(得分:3)

在两个示例中,这都是通道的行为。通道分为缓冲和非缓冲两种。

缓冲的通道具有一些在通道内存储项目的能力。就像一个缓冲区。

未缓冲的通道在该通道中没有存储项目的位置,这意味着在将任何内容发送到未缓冲的通道中或将任何内容写入未缓冲的通道之前,需要先读取一些内容。这意味着您必须在通道的每一端都有一个goroutine,一个准备发送,一个准备接收。

在第一个示例中:

func main() {
    ch := make(chan int)
    ch <- 1
    fmt.Println(<-ch)
}

您有1个goroutine(主要的go例程)。这个单一的goroutine试图将一个值写入通道ch <- 1,但是没有另一个goroutine准备在同一时间接收值函数fmt.Println(<-ch),但此操作在写入后 之后执行,但是写入无法成功,因为与写入同时没有就绪的读取。您需要2个goroutines!

在下一个示例中:

func main() {
    var wg sync.WaitGroup

    ch := make(chan int)

    wg.Add(1)
    go func() {
        <-ch 
        wg.Done()
    }()

    wg.Wait()
}

在操场上进行测试时,也会惊慌失措。这是因为您已经使用go func() {... }创建了第二个goroutine,试图从通道中读取值,但是没有什么东西可以在同一时间发送 。因此,所有goroutine都被阻止,并且出现了恐慌。

此示例要注意的一件事是,如果关闭了通道ch,并且在发生读取之前使用close(ch),则读取将始终成功。它将能够从通道读取零值(在这种情况下为0)。

正如您所指出的那样,改写上面的示例以在匿名函数ch<-中进行写操作也会引起恐慌,因为因为没有第二个例程可以在同一时间接收该值!

这是一个简化的,经过修改的工作版本,显示您需要在无缓冲通道上同时进行读取和写入。

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