使用缓冲和非缓冲通道遇到麻烦
Posted
技术标签:
【中文标题】使用缓冲和非缓冲通道遇到麻烦【英文标题】:Go trouble with buffered and unbuffered channels 【发布时间】:2020-10-23 09:30:29 【问题描述】:我对 Go 中缓冲通道和非缓冲通道之间的区别有点困惑。例如,下面的代码执行良好:
package main
import "fmt"
func main()
messages := make(chan string)
go func() messages <- "ping" ()
msg := <-messages
fmt.Println(msg)
另一方面,当我将"ping"
传递给常规函数中的消息时,会出现死锁。
package main
import "fmt"
func main()
messages := make(chan string)
func() messages <- "ping" ()
msg := <-messages
fmt.Println(msg)
最后,当我使用缓冲通道时,这是固定的,就像这样
package main
import "fmt"
func main()
messages := make(chan string, 1)
func() messages <- "ping" ()
msg := <-messages
fmt.Println(msg)
我很困惑为什么第二个案例失败了。 Go By Example 是这样说的
默认情况下,通道是无缓冲的,这意味着如果有相应的接收 (
在所有三种情况下,msg
不是 messages
的接收器吗?
【问题讨论】:
【参考方案1】:如果未从中读取未缓冲的通道,则它们会阻塞。缓冲通道在达到容量之前不会阻塞。
您的第一个示例实际上启动了一个单独的 go 例程,该例程执行尝试将“ping”写入消息通道的函数。它将阻塞,直到从消息通道读取的语句执行。从消息通道读取的语句能够被命中,因为该函数位于单独的 goroutine 上。
您的第二个示例声明并调用了一个尝试写入消息通道的函数,但该通道永远不会准备好写入,因为您正在同一个主执行线程上执行。从消息通道读取的语句永远不会命中,因为您在写入通道时被阻止。
第三个例子,通道被缓冲并且可以被写入,因为它可以在阻塞之前接受 1 个值。
【讨论】:
【参考方案2】:您可能已经浏览了那里的一个关键词:“如果有相应的接收(准备好接收发送的值”。
在您的第一个示例中,接收器与发送器同时运行,因此当发送器发送时,接收器此时已准备好接收。在第二个示例中,它们不同时运行,因此当发送方发送时,没有准备好接收(因为接收操作在匿名函数返回之前不会运行),并且发送阻塞。
【讨论】:
【参考方案3】:在第一个示例中,嵌套函数是从另一个 goroutine 调用的。该函数开始运行并阻塞等待写入通道。主 goroutine 也运行,并从通道读取,释放第二个 goroutine 中的块。
在第二个例子中,嵌套函数由 main 调用,main 等待它返回。由于通道没有缓冲,写操作阻塞,这意味着主协程被阻塞,没有其他协程,所以死锁。
在第三个示例中,通道被缓冲,因此第一次写入不会阻塞。
【讨论】:
以上是关于使用缓冲和非缓冲通道遇到麻烦的主要内容,如果未能解决你的问题,请参考以下文章