缓冲的频道并关闭它

时间:2018-06-22 04:13:01

标签: go

我有一段代码根据我如何放置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试图向关闭的通道写入更多元素时为什么不抛出异常? / li>
  3. 我在Pos3上关闭时,尽管打印了所有5个整数,但都得到了一个异常fatal error: all goroutines are asleep - deadlock!。为什么会这样?

1 个答案:

答案 0 :(得分:3)

  

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

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

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

  

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

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

因此,在您的代码中,将$ cat file2.txt This is a tale Of Captain Jack Sparrow A Pirate So Brave On the Seven Seas. 放入Pos1中,确实是正确的方法。当您将其放入Pos3中时,for循环将等待通道关闭,这只能在for循环本身之后发生,因此它是一个死锁。

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