为什么Go的频道可以关闭两次? [英] Why Go's channel can close twice?

查看:142
本文介绍了为什么Go的频道可以关闭两次?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  

当我做一些实践代码时,我遇到了一个问题, // jobs.go

包裹主要

进口(
fmt


func main(){
fmt.Println(Hello,playground)
工作:= make(chan int,5)
完成:= make(chan bool)

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

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

$(








$ b $



输出:

 〜go run jobs.go 
您好,游乐场
收到关闭:0 false
收到关闭:0 false

但是,当我手动关闭频道两次时,我得到了 panic:关闭了频道

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

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



不受限制,从一个封闭的渠道接收:


A接收在关闭频道上进行的操作始终可以立即进行,产生元素类型的 zero value




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



你开始了第二个goroutine, code> for 循环,无法终止。所以这个循环会一直持续到 main()函数 - 它运行在并发的主函数返回中。由于循环首先从作业接收,它等待主协程关闭它(此接收操作只能然后继续)。然后主要的goroutine希望从 done 接收,等到第二个goroutine发送一个值。然后主办公室随时自由终止。运行循环的第二个goroutine可能会从作业中收到一个附加值,因为它已关闭,但随后发送 done 将会因为没有人接收它(而且它没有缓冲)。



从频道接收直到关闭为止,通常使用完成,范围,如果频道关闭,则退出:

 对于j:=范围作业{
fmt.Println(received:,j)
完成< - true
}

当然这会在你的情况下导致死锁,因为循环体永远不会被触及,因为没有人在作业中发送任何东西,所以循环永远不会进入它的主体在 done 上发送一个值,这是主要的goroutine等待的。


When I am do some go practices code, I encounter a problem that a channel can be closed twice like this:

// 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
}

Output:

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

But when I close the channel twice manually, I got panic: close of closed channel.

Why the code above can receive close twice?

解决方案

A channel can only be closed once, attempting to close a closed channel panics.

But receiving from a closed channel is not limited, receiving from a closed channel:

A receive operation on a closed channel can always proceed immediately, yielding the element type's zero value after any previously sent values have been received.

A Go app runs until its main goroutine runs (given "normal" circumstances), or from another point of view: a Go app terminates when its main goroutine terminates, that is, the main() function returns. It does not wait for other non-main goroutines to complete.

You started a 2nd goroutine with an endless for loop, with no way of terminating. So that loop will keep going until the main() function –which runs in the concurrent, main goroutine– returns. Since the for loop first receives from jobs, it waits for the main goroutine to close it (this receive operation can only then proceed). Then the main goroutine wants to receive from done, so that waits until the 2nd goroutine sends a value on it. Then the main goroutine is "free" to terminate at any moment. Your 2nd goroutine running the loop may receive an additional value from jobs since it's closed, but the subsequent send on done will block as there are nobody receiving from it anymore (and it's unbuffered).

Receiving from a channel until it's closed is normally done using for range, which exits if the channel is closed:

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

Of course this would cause a deadlock in your case, as the loop body would never be reached as nobody sends anything on jobs, and so the loop would never enter its body to send a value on done which is what the main goroutine waits for.

这篇关于为什么Go的频道可以关闭两次?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆