Golang中的垃圾通道?

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

Garbage channel in Golang?

问题

我在我的函数中接受一个done通道,如果调用者关心的话,这对于通知调用者某个异步过程何时完成非常有用。这在编写单元测试时非常有用。

如果我的调用者不需要这个功能,有没有一种惯用的方法来传递一个垃圾通道,其中所有发送到它的值都会立即被丢弃?最初我很惊讶地发现发送nil不起作用(发送到nil会阻塞发送者)。我可以想到一种粗糙的实现方式(比如一个始终运行的goroutine从该通道中消费),但我很想写出这样的代码:

func myFunc(foo int, done chan bool) {
    ....
}

func main() {
    myfunc(4, _)
}

有没有一种简单的方法来实现这个?

英文:

I accept a done channel in my function, which is nice for notifying callers when some async process has finished, if they care. This is very useful for writing unit tests for example.

If my caller doesn't need this functionality, is there an idiomatic way to pass a garbage channel, where all values sent to it are discarded immediately? I was initially surprised sending nil didn't work (sending to nil blocks the sender). I can think of one gross implementation (like a goroutine always running that consumes from this channel), but I would love to write something like:

func myFunc(foo int, done chan bool) {
    ....
}

func main() {
    myfunc(4, _)
}

Is there a simple way to accomplish this?

答案1

得分: 4

你可以使用select语句来避免阻塞发送操作:

select {
case done <- true:
default:
}

另一种选择,也是我更喜欢的一种方式,是不要通过done通道发送数据,而是关闭通道。这会导致所有被阻塞的接收操作立即返回,同时返回的是相同的值(而不是需要发送每个接收操作的值),并且如果没有监听者,防止发送退出信号的函数被阻塞。这还允许你将通道替换为chan struct{},这很好,因为struct{}的大小为0。

值得注意的是,关闭一个nil通道会导致panic,所以你仍然需要对其进行nil检查。

编辑:这是我更喜欢的写法:

func myFunc(foo int, done chan<- struct{}) {
    if done != nil {
        defer close(done)
    }
    ....
}

编辑2:你甚至可以将它变成可变参数,这样可以省略done通道,或者提供多个done通道。

func myFunc(foo int, done ...chan<- struct{}) {
    for _, d := range done {
        if d != nil {
            defer close(d)
        }
    }
    ....
}

使用示例:

myFunc(1)
myFunc(2, ch1)
myFunc(3, ch2, ch3, ch4)
英文:

You can use a select on the send to avoid blocking:

select {
case done &lt;- true:
default:
}

The other alternative, and my preferred one, is to not send things over done channels. Instead, close the channel. This causes all blocked receive operations to instantly return, all at the same time (instead of having to send each one a value), and prevents blocking in the function sending the quit signal if nothing is listening. This also lets you replace the channel with a chan struct{}, which is nice, because a struct{} has 0 size.

Still, worth noting that closing a nil channel, instead of blocking, panics, so you still have to do a nil check on it.

Edit: my preferred style on this:

func myFunc(foo int, done chan&lt;- struct{}) {
    if done != nil {
        defer close(done)
    }
    ....
}

Edit 2: You could even make it a variadic, which allows the done channel to be omitted, or more than one to be provided.

func myFunc(foo int, done ...chan&lt;- struct{}) {
    for _, d := range done {
        if d != nil {
            defer close(d)
        }
    }
    ....
}
----
myFunc(1)
myFunc(2, ch1)
myFunc(3, ch2, ch3, ch4)

答案2

得分: 0

这是代码的翻译结果:

func DoAllTheThings(done ...chan<- bool) {

    // 做一些事情

    // 全部完成!
    if len(done) >= 1 {
        done[0] <- true
    }
    return
}

如果在参数中没有给出通道那么就不会发送任何内容显然调用者需要设置一个监听器来接收通道的消息你可以这样调用它

func main() {
    DoAllTheThings()
}

或者

func main() {
    ch := make(chan bool)
    go func() {
        done := <-ch
        if done {
            fmt.Println("Yay!")
        }
    }()
    DoAllTheThings(ch)
}

你可以在这里查看代码:https://play.golang.org/p/Ktc977gYpA

英文:

How about this:

func DoAllTheThings(done ...chan&lt;- bool) {

    // do the things

    // all done!
    if len(done) &gt;= 1 {
        done[0] &lt;- true
    }
    return
}

If no channel is given in arguments, then nothing is sent. It would be up to the caller to set up a listener for the channel, obviously. Then you could call it as:

func main() {
    DoAllTheThings()
}

or

func main() {
    ch := make(chan bool)
    go func() {
        done := &lt;- ch
        if done {
            fmt.Println(&quot;Yay!&quot;)
        }
    }()
    DoAllTheThings(ch)
}

https://play.golang.org/p/Ktc977gYpA

huangapple
  • 本文由 发表于 2016年12月14日 05:47:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/41131115.html
匿名

发表评论

匿名网友

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

确定