为什么Go的频道可以关闭两次?

时间:2017-05-03 07:12:44

标签: go channel

当我做一些去练习代码时,我遇到一个问题,一个频道可以这样关闭两次:

// jobs.go

package main

import (
   "fmt"
)

func main() {
    fmt.Println("Hello, playground")
    jobs := make(chan int, 5)
    done := make(chan bool)

    go func() {
        for {
            j,more := <-jobs

            fmt.Println("receive close: ", j, more)
            done <- true   
        }
    }()

    close(jobs)
    <- done
}

输出:

~ go run jobs.go
Hello, playground
receive close:  0 false
receive close:  0 false

但是当我手动关闭频道两次时,我得到panic: close of closed channel

为什么上面的代码可以接收两次?

3 个答案:

答案 0 :(得分:3)

一个频道只能关闭一次,试图关闭一个封闭的频道恐慌。

但来自封闭频道的receiving不受限制,从封闭频道接收:

  

closed频道上的接收操作始终可以立即进行,在收到任何先前发送的值后,会产生元素类型zero value

Go应用程序运行直到其主要goroutine运行(给定“正常”情况),或者从另一个角度来看:Go应用程序在其主要goroutine终止时终止,即main()函数返回。它不会等待其他非main goroutines完成。

你开始了一个无尽的for循环的第二个goroutine,无法终止。因此循环将一直持续到main()函数 - 它在并发的主要goroutine-返回中运行。由于for循环首先从jobs接收,它等待主goroutine关闭它(此接收操作只能继续)。然后主goroutine想要从done接收,以便等到第二个goroutine发送一个值。然后主要的goroutine“随意”随时终止。运行循环的第二个goroutine可能会从jobs收到一个额外的值,因为它已关闭,但done上的后续发送将被阻止,因为没有人再接收它(并且它没有缓冲)。

从频道接收直到关闭通常是使用for range完成的,如果频道已关闭则退出:

for j := range jobs {
    fmt.Println("received: ", j)
    done <- true
}

当然这会导致你的情况出现死锁,因为在jobs上没有人发送任何内容时永远不会到达循环体,因此循环永远不会进入其身体以在{{1}上发送值这是主要的goroutine等待的东西。

答案 1 :(得分:1)

jobs频道未关闭两次。它只在调用close(jobs)时关闭一次。您看到的输出是由于goroutine和主线程的执行方式。

当goroutine开火时,它不会立即开始运行。相反,程序控制转到这一部分:

  close(jobs) // <--
  <- done
}

jobs已关闭。主线程然后在done上的下一次接收时挂起。

现在进行上下文切换,goroutine开始运行。它从已关闭的jobs读取,打印相应的值(falsemore,表示已关闭的频道),并沿true发送done

但是,循环能够再次执行,而下一个goroutine会在done上发送。现在main再次唤醒,在done收到并终止该程序。

答案 2 :(得分:1)

去频道没关闭twise。你传递完毕&lt; - 首次打印后的真实

j,more := <-jobs

fmt.Println("receive close: ", j, more)
done <- true

所以它打印了两次

receive close:  0 false
receive close:  0 false

如果您在打印前使用完成&lt; - true,那么它将仅打印一次,将被关闭。

done <- true
j,more := <-jobs

fmt.Println("receive close: ", j, more)

输出:

receive close:  0 false