英文:
Golang: cannot send on channel
问题
为什么它没有在通道上发送信号并阻塞执行?我该如何使这个组合工作,以便我可以向MoneyDive()
发送信号并继续执行?
package main
import (
"fmt"
)
type Quack func(ch chan bool)
type DagobertDuck struct {
quack Quack
}
func (self *DagobertDuck) MoneyDive() {
ch := make(chan bool)
self.quack(ch)
b := <-ch
if b {
fmt.Println("true")
} else {
fmt.Println("false")
}
}
func mockQuack(ch chan bool) {
fmt.Println("mockQuack start")
ch <- true
fmt.Println("mockQuack done")
}
func main() {
dd := DagobertDuck{quack: mockQuack}
dd.MoneyDive()
}
链接:https://play.golang.org/p/1omlb7u6-A
英文:
Why is it not sending on the channel and blocking the execution? How can I make this constellation work so that I can send a signal into MoneyDive()
and continue execution?
package main
import (
"fmt"
)
type Quack func(ch chan bool)
type DagobertDuck struct {
quack Quack
}
func (self *DagobertDuck) MoneyDive() {
ch := make(chan bool)
self.quack(ch)
b := <-ch
if b {
fmt.Println("true")
} else {
fmt.Println("false")
}
}
func mockQuack(ch chan bool) {
fmt.Println("mockQuack start")
ch <- true
fmt.Println("mockQuack done")
}
func main() {
dd := DagobertDuck{quack: mockQuack}
dd.MoneyDive()
}
答案1
得分: 8
因为你有一个非缓冲通道,只有在有另一个准备好接收的goroutine时,才能在非缓冲通道上发送值而不阻塞。
由于你只有一个goroutine,它被阻塞了。解决方法很简单:在一个新的goroutine中启动Quack.quack()
方法:
go self.quack(ch)
然后输出结果(在Go Playground上尝试):
mockQuack start
mockQuack done
true
另一个选项是不启动新的goroutine,而是创建一个带有缓冲区的通道,这样它可以在没有准备好接收的接收器的情况下保存一些值:
ch := make(chan bool, 1) // 带有缓冲区的通道,缓冲区大小为1
这样就创建了一个通道,它可以在没有准备好接收的接收器的情况下“存储”一个值。除非首先从通道接收到该值(或者准备好接收该值的接收器),否则对通道的第二次发送也会被阻塞。
在Go Playground上尝试这个带缓冲区的通道版本。
规范中的相关部分:发送语句:
> 在通信开始之前,通道和值表达式都会被求值。**如果接收器准备好接收,发送可以进行,非缓冲通道上的发送可以进行。**如果缓冲通道有空间,发送可以进行。关闭的通道上的发送会导致运行时恐慌。在_nil_通道上的发送会永远阻塞。
注意:
根据接收到的值,你打印true
或false
。这可以用一行代码完成,不需要if
语句:
fmt.Println(b)
你甚至可以摆脱b
局部变量,直接打印接收到的值:
fmt.Println(<-ch)
另外,我假设你使用通道是因为你想玩一下,但在你的情况下,mockQuack()
可以简单地返回bool
值,而不使用通道。
英文:
Because you have an unbuffered channel, and you can only send a value on an unbuffered channel without blocking if there is another goroutine which is ready to receive from it.
Since you only have 1 goroutine, it gets blocked. Solution is simple: launch your Quack.quack()
method in a new goroutine:
go self.quack(ch)
Then the output (try it on the Go Playground):
mockQuack start
mockQuack done
true
Another option is to not launch a new goroutine but make a buffered channel, so it can hold some values without any receivers ready to receive from it:
ch := make(chan bool, 1) // buffered channel, buffer for 1 value
This creates a channel which is capable of "storing" one value without any receivers ready to receive it. A second send on the channel would also block, unless the value is received from it first (or a receiver ready to receive a value from it).
Try this buffered channel version on the Go Playground.
Relevant section from the spec: Send statements:
> Both the channel and the value expression are evaluated before communication begins. Communication blocks until the send can proceed. A send on an unbuffered channel can proceed if a receiver is ready. A send on a buffered channel can proceed if there is room in the buffer. A send on a closed channel proceeds by causing a run-time panic. A send on a nil channel blocks forever.
Notes:
Based on the received value you print true
or false
. This can be done with a single line, without the if
statement:
fmt.Println(b)
You can even get rid of the b
local variable, and print the received value right away:
fmt.Println(<-ch)
Also I assume you used channels because you wanted to play with them, but in your case mockQuack()
could simply return the bool
value, without the use of channels.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论