缓冲通道并关闭它

问题描述 投票:-1回答:1

我有一段代码,我试图理解基于我如何把close调用和位置

func main() {
    ch := make(chan int, 2)

    go func(ch chan int) {
        for i := 1; i <= 5; i++ {
            ch <- i
            fmt.Println("Func goroutine sends data: ", i)
        }
        //Pos1 - Works perfectly
        //close(ch)
    }(ch)

    fmt.Println("Main goroutine sleeps 2 seconds")
    time.Sleep(time.Second * 2)

    fmt.Println("Main goroutine begins receiving data")
    //Pos2 - Puts in only 2 ints 1 and 2 and then prints only that
    //close(ch)
    for d := range ch {
        fmt.Println("Main goroutine received data:", d)
    }
    //Pos3 - Throws fatal error
    close(ch)
}

我一直在努力理解和阅读博客,但仍然无法理解某些事情

  1. 当我把收盘放在Pos1时,它运作正常。但我不确定它为什么会起作用。缓冲区在任何给定时间都不能容纳2个以上的元素,因此当写入2个元素时,循环将阻塞,直到主路由执行读取。但是我想在缓冲通道上做一个范围,范围函数必须事先知道要迭代多少个元素并且必须关闭该通道。为什么close在这个位置工作?
  2. 当我把它作为位置2时,它只打印2个有意义的元素但为什么for loop在尝试将更多元素写入已关闭的通道时不会抛出异常?
  3. 当我在Pos3关闭时,我得到一个例外fatal error: all goroutines are asleep - deadlock!虽然打印了所有5个整数。这是为什么?
go
1个回答
3
投票

但是我想在缓冲通道上做一个范围,范围函数必须事先知道要迭代多少个元素并且必须关闭该通道。

这种假设是错误的,是所有误解的根源。

Go Spec:https://golang.org/ref/spec#For_statements中描述了通道上测距的行为

对于通道,产生的迭代值是在通道上发送的连续值,直到通道关闭。如果通道为nil,则范围表达式将永久阻塞。

在评估for语句并且语句不需要知道元素数量时,不需要关闭通道。

所以,在你的代码中,当你将close放在Pos1中时,它确实是正确的方法。当你把它放在Pos3中时,for循环等待关闭的通道,这只能在for循环本身之后发生,所以它是一个死锁。

close放在Pos2中是有缺陷的,行为有点棘手。可能会引发错误,但可以输出两个数字。这是因为当在for循环之前关闭通道时,循环可以无阻塞地运行,然后main()返回。当main()返回时,Go程序结束。是否引发错误仅取决于调度程序是否在进程之间切换到goroutine,这是不能保证的。

© www.soinside.com 2019 - 2024. All rights reserved.