为什么将通道输入参数设置为nil?

huangapple go评论71阅读模式
英文:

Why set a channel input parameter to nil?

问题

我需要一些帮助来理解Go泛型提案中的示例部分。特别是我在提案的示例部分“通道(Channels)”中遇到了两段代码的困惑:

第一个我不理解的点在于func Merge[T any](c1,c2 <-chan T) <-chan T的定义中:该函数实例化了一个chan T变量来保存结果,然后启动一个处理实际合并操作的goroutine。

特别地,在至少一个输入通道(要合并的通道)不为nil的情况下,会有一个无限循环运行。

代码如下:

// Merge merges two channels of some element type into a single channel.
func Merge[T any](c1, c2 <-chan T) <-chan T {
    r := make(chan T)
    go func(c1, c2 <-chan T, r chan<- T) {
        defer close(r)
        for c1 != nil || c2 != nil {
            select {
            case v1, ok := <-c1:
                if ok {
                    r <- v1
                } else {
                    c1 = nil
                }
            case v2, ok := <-c2:
                if ok {
                    r <- v2
                } else {
                    c2 = nil
                }
            }
        }
    }(c1, c2, r)
    return r
}

在循环中,select语句遍历输入通道以获取有效接收到的值(vn, ok := <-cn表达式),然后出现了一个奇怪的部分:

如果通道发送了有效值,则进行合并并完成(r <- vn),但是如果通道报告它刚刚提供了其元素类型的零值(ok == false),那么case分支的else部分的代码会执行一些我无法理解的操作:它将通道变量设置为nil

那么,为什么合并的goroutine可以将其输入通道设置为nil呢?这不应该是负责输入通道cn的goroutine的责任吗?显然我漏掉了一些东西,请给我解释一下。

英文:

I need some help making sense of the Go generics proposal examples.

In particular I am having trouble-so far-understanding two bits of code from the examples section of the proposal entitled "Channels":

The first point I don't get is found in the definition of func Merge[T any](c1,c2 &lt;-chan T) &lt;-chan T: the function instantiates a chan T variable that will hold the result and then it spins up a goroutine that will handle the actuall merging.

In particular, there is an infinite loop that runs as long as at least one of the input channels (to be merged) is not nil.

Code:

// Merge merges two channels of some element type into a single channel.
func Merge[T any](c1, c2 &lt;-chan T) &lt;-chan T {
	r := make(chan T)
	go func(c1, c2 &lt;-chan T, r chan&lt;- T) {
		defer close(r)
		for c1 != nil || c2 != nil {
			select {
			case v1, ok := &lt;-c1:
				if ok {
					r &lt;- v1
				} else {
					c1 = nil
				}
			case v2, ok := &lt;-c2:
				if ok {
					r &lt;- v2
				} else {
					c2 = nil
				}
			}
		}
	}(c1, c2, r)
	return r
}

In the loop, a select statement combs through the input channels for validly received values (the vn,ok:=&lt;-cn expressions) and then comes the weird part:

If the channel sends valid values, merge and be done with it (r&lt;-vn) but if the channel reports that it just supplied the zero value for its element type (ok==false) then the code in the else branch of both case branches does something incomprehensible (to me): it sets the channel variable to nil!

So, why does the merging goroutine is allowed to set its input channels to nil. Isn't this supposed to be the obligation of the goroutine that is responsible for input channels cn? I'm obviously missing something, please enlighten me.

答案1

得分: 2

将通道设置为nil将阻止从那个case开始执行。注意:只有函数参数被设置为nil,它是函数的局部变量,通道对象保持不变。

该函数应该循环,直到两个输入通道都关闭,这可能不会同时发生。一旦通道关闭,从中接收数据就可以立即进行。因此,如果一个通道关闭,从它接收的case将始终从那里执行,立即,不必要地使用高CPU,并且它还可能阻止从其他未关闭的通道接收(如果多个case都准备好,将会伪随机选择一个,参见https://stackoverflow.com/questions/47645808/how-does-select-work-when-multiple-channels-are-involved/47648910#47648910)。

另一方面,如果一个(关闭的)通道被设置为nil,它将从select中被移除,因为从nil通道接收数据会永远阻塞(所以只有另一个通道将从那里被监视)。有关通道公理,请参见https://stackoverflow.com/questions/39015602/how-does-a-non-initialized-channel-behave/39016004#39016004。

英文:

Setting the channel to nil will prevent from that case getting executed from there on. Note: only the function parameter is set to nil, which is a local variable to the function, the channel object is left intact.

The function should loop until both input channels are closed, which will likely not happen at the same time. Once a channel is closed, receiving from it can proceed immediately. So if a channel is closed, the case receiving from it could be executed always from there on, immediately, unnecessarily using high CPU, and also it could prevent receiving from the other, non-closed channel (if multiple cases are ready, one is chosen pseudo-randomly, see https://stackoverflow.com/questions/47645808/how-does-select-work-when-multiple-channels-are-involved/47648910#47648910).

On the other hand, if a (closed) channel is set to nil, it will essentially be taken out from select, because receiving from a nil channel blocks forever (so only the other channel will be monitored from there on). For channel axioms, see https://stackoverflow.com/questions/39015602/how-does-a-non-initialized-channel-behave/39016004#39016004

huangapple
  • 本文由 发表于 2022年8月19日 14:23:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/73412598.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定