为了账号安全,请及时绑定邮箱和手机立即绑定

为什么在同一个 goroutine 中使用无缓冲通道会导致死锁?

为什么在同一个 goroutine 中使用无缓冲通道会导致死锁?

Go
手掌心 2021-06-16 21:01:23
我确信对这种微不足道的情况有一个简单的解释,但我是go并发模型的新手。当我运行这个例子时package mainimport "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 +0x52exit status 2为什么 ?包裹c <-在 agoroutine中使示例按预期运行package mainimport "fmt"func main() {    c := make(chan int)            go func(){       c <- 1    }()    fmt.Println(<-c)}再说一遍,为什么?拜托,我需要深入的解释,而不仅仅是如何消除死锁和修复代码。
查看完整描述

3 回答

?
喵喵时光机

TA贡献1846条经验 获得超7个赞

在无缓冲通道中写入通道不会发生,直到必须有一些接收器等待接收数据,这意味着在下面的例子中


func main(){

    ch := make(chan int)

    ch <- 10   /* Main routine is Blocked, because there is no routine to receive the value   */

    <- ch

}

现在如果我们有其他 goroutine,同样的原则适用


func main(){

  ch :=make(chan int)

  go task(ch)

  ch <-10

}

func task(ch chan int){

   <- ch

}

这将起作用,因为任务例程正在等待数据被使用,然后写入发生到无缓冲通道。


为了更清楚,让我们交换主函数中第二条和第三条语句的顺序。


func main(){

  ch := make(chan int)

  ch <- 10       /*Blocked: No routine is waiting for the data to be consumed from the channel */

  go task(ch)

}

这会导致死锁


所以简而言之,只有当有一些例程等待从通道读取时才会写入无缓冲通道,否则写入操作将被永远阻塞并导致死锁。


注意:相同的概念适用于缓冲通道,但在缓冲区已满之前不会阻止发送方,这意味着接收方不一定要与每个写操作同步。


因此,如果我们有大小为 1 的缓冲通道,那么您上面提到的代码将起作用


func main(){

  ch := make(chan int, 1) /*channel of size 1 */

  ch <-10  /* Not blocked: can put the value in channel buffer */

  <- ch 

}

但是如果我们给上面的例子写更多的值,那么就会发生死锁


func main(){

  ch := make(chan int, 1) /*channel Buffer size 1 */

  ch <- 10

  ch <- 20 /*Blocked: Because Buffer size is already full and no one is waiting to recieve the Data  from channel */

  <- ch

  <- ch

}


查看完整回答
反对 回复 2021-06-28
?
芜湖不芜

TA贡献1796条经验 获得超7个赞

在这个答案中,我将尝试解释错误消息,通过它我们可以稍微了解 go 在通道和 goroutines 方面的工作原理


第一个例子是:


package main


import "fmt"


func main() {

    c := make(chan int)    

    c <- 1   

    fmt.Println(<-c)

}

错误信息是:


fatal error: all goroutines are asleep - deadlock!

在代码中,根本没有 goroutines(顺便说一句,这个错误是在运行时,而不是编译时)。当 go 运行此行时c <- 1,它希望确保通道中的消息将在某处(即<-c)收到。Go 不知道此时是否会收到频道。因此,go 将等待正在运行的 goroutine 完成,直到发生以下任一情况:


所有的 goroutine 都完成了(睡着了)

goroutine 之一尝试接收通道

在第 1 种情况下,go 会出错并显示上面的消息,因为现在 go 知道 goroutine 无法接收通道并且它需要一个。


在第 2 种情况下,程序将继续,因为现在知道收到了这个频道。这解释了 OP 示例中的成功案例。


查看完整回答
反对 回复 2021-06-28
  • 3 回答
  • 0 关注
  • 231 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信