我相信对于这种微不足道的情况有一个简单的解释,但是我对go并发模型还是个新手.

当我运行这个例子时

package main

import "fmt"

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

我得到了这个错误:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
    /home/tarrsalah/src/go/src/github.com/tarrsalah/tour.golang.org/65.go:8 +0x52
exit status 2

为什么?


goroutine中包装c <-会使示例按照我们的预期运行

package main

import "fmt"

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

再说一遍,为什么?

拜托,我需要深入的解释,而不仅仅是如何消除死锁和修复代码.

推荐答案

the documentation:

如果通道未缓冲,则发送器会阻塞,直到接收器接收到该值.如果通道有缓冲区,则发送器只会阻止,直到 已复制到缓冲区;如果缓冲区已满,这意味着 等待,直到某个接收器已经检索到一个值.

反过来说:

  • 当通道已满时,发送方等待另一个Goroutine通过接收
  • 您可以将无缓冲通道视为始终满的通道:必须有另一个Goroutine才能接收发送者发送的内容.

这条线

c <- 1

由于通道未缓冲而阻塞.由于没有其他goroutine可以接收该值,因此情况无法解决,这是一个死锁.

您可以通过将通道创建更改为

c := make(chan int, 1) 

这样,在阻塞通道之前,通道中有容纳一个项目的空间.

但这不是并发的意义所在.通常,您不会使用没有其他Goroutine的通道来处理您放入的内容.您可以这样定义接收Goroutine:

func main() {
    c := make(chan int)    
    go func() {
        fmt.Println("received:", <-c)
    }()
    c <- 1   
}

Demonstration

Go相关问答推荐

如何创建两个连接的io.ReadWriteClosers以用于测试目的

我不能让GIO画一个按钮

Go:拆分一个由逗号分隔的键/值对字符串,并在给定的键/价值对中嵌入可能的逗号

如何为循环扫描的bufio scanner 设置超时?

如何使用 html/template 在 golang 中运行一个范围内的范围

Go 中如何判断 struct 体是否包含另一个 struct 体?

转到 bufio.Writer、gzip.Writer 并上传到内存中的 AWS S3

从 ApiGateway 中的 lambda Go 返回 Json

Golang text/template中的startswith函数 - 入门教程

如何在 fyne-io/fyne 中设置文本的字母间距?

Go struct 匿名字段是公开的还是私有的?

Golang Gin 绑定请求正文 XML 到 Slice

从 os.stdout 读取

为什么在单独的 go routine 中需要 wg.Wait() 和 close() ?

如何使用 Go 获取 X11 中的窗口列表

没有堆栈跟踪的 go 程序崩溃是什么意思?

Go 中 SDL Surface 的 OpenGL 纹理

无限期运行 Go routine (完成后重新启动)

如何在golang中使用ozzo验证进行时间最大验证

为什么从 Go 1.17 开始在 go.mod 中有两个require块?