英文:
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 <- 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<- 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<- 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<- bool) {
// do the things
// all done!
if len(done) >= 1 {
done[0] <- 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 := <- ch
if done {
fmt.Println("Yay!")
}
}()
DoAllTheThings(ch)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论