首页 > 解决方案 > Go 教程:通道和缓冲通道

问题描述

当通过另一个 go 例程发送第二个值并且未收到发送的第一个值时,为什么通道c没有缓冲出来?

package main
import "fmt"

func sum(s []int, c chan int) {
    sum := 0
    for _, v := range s {
        sum += v
    }
    c <- sum // send sum to c   
}

func main() {
    s := []int{7, 2, 8, -9, 4, 0}
    c := make(chan int)
    go sum(s[:len(s)/2], c)
    go sum(s[len(s)/2:], c)
    x, y :=  <-c ,<-c// receive from c
    fmt.Println(x,y ,x+y)
}

我所期待的是一个错误-

致命错误:所有 goroutine 都处于休眠状态 - 死锁!

当缓冲区已满时出现块时,会发生这种情况。由于通道 c 的大小为 1,因此发送第二个值应该会给出上述错误。

上面的代码发生了什么?

标签: gobufferchannelchannels

解决方案


仅仅因为写入不能立即成功,只要有其他 goroutine 可以运行,您就不会出现“死锁”错误。

让我们想象一个调度模型,其中一个go函数立即启动 goroutine,并在让步给其他人之前尽可能多地向前推进。然后会发生这种情况:

  1. 程序将调用sum()列表的前半部分,计算总和,并尝试将其写入通道,但由于没有侦听器,因此会阻塞。
  2. 程序将调用sum()列表的后半部分,计算总和,并尝试将其写入通道,但由于没有侦听器,它会阻塞。
  3. main()将尝试从通道读取,唤醒之前的 goroutine 之一,并从中获取值。
  4. main()将尝试从通道读取,唤醒另一个阻塞的 goroutine,并从中获取值。
  5. 没有人再阻塞通道输入或输出,所有的 goroutines(包括main())都可以运行完成。

如果你假装go只是在后台安排一些事情并继续运行主 goroutine,你可以做同样的练习。重要的是,一旦在同一通道上进行了配对读写,两者都会继续进行。


推荐阅读