【问题标题】:Buffered channel and closing it缓冲通道并关闭它
【发布时间】:2018-12-01 11:55:51
【问题描述】:

我有一个 sn-p 代码,我试图根据我如何放置 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】:

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

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

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

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

    for 语句求值时不需要关闭通道,语句不需要知道元素个数。

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

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

    【讨论】:

    • 感谢Pos3 placement,跟进问:由于anon func永远不会关闭它,而是在写入5个元素后退出,是什么导致main函数中的for循环甚至退出无限循环,因为通道没有关闭,range 无法知道是否以及何时会出现更多数据。你也能解释一下吗?
    • @curiousengineer 在这种情况下它永远不会退出 for 循环。
    • 但是为什么prg能够抛出死锁错误而不是永远休眠呢?我的意思是主函数 shud 永远卡住了,我绝不能让我的 bash 提示返回致命异常。对吗?
    • @curiousengineer 不。发生的不是像for {} 这样的无限循环,而是一个块。这就像从控制台读取,只是控制台是一个通道。所以 Go 决定在 for 循环等待 value 的同时切换到其他地方(goroutine),然后发现没有其他地方可以去。所以它实际上没有代码可以运行并报告死锁。
    • @curiousengineer 你参加过 Go Tour 吗?
    猜你喜欢
    • 2014-01-07
    • 2016-06-20
    • 2014-01-30
    • 1970-01-01
    • 1970-01-01
    • 2012-01-15
    • 2021-10-13
    • 2017-09-06
    • 2016-08-30
    相关资源
    最近更新 更多