我观看了关于Advanced Go Concurrency Patterns的精彩视频。最初,Sameer Ajmani展示了一个乒乓球应用程序。
package main
import (
"fmt"
"time"
)
type Ball struct{ hits int }
func main() {
table := make(chan *Ball)
go player("ping", table)
go player("pong", table)
table <- new(Ball) // game on; toss the ball
time.Sleep(1 * time.Second)
fmt.Println(<-table) // game over; grab the ball
}
func player(name string, table chan *Ball) {
for {
ball := <-table
ball.hits++
fmt.Println(name, ball.hits)
time.Sleep(100 * time.Millisecond)
table <- ball
}
}
代码如何工作,我理解为90%。它们是两个goroutines,它们在主线程休眠期间发送给对方的消息,ping和pong。
然后我尝试关注
package main
import (
"fmt"
"time"
)
type Ball struct{ hits int }
func main() {
table := make(chan *Ball)
go player("ping", table)
go player("pong", table)
table <- new(Ball) // game on; toss the ball
time.Sleep(1 * time.Second)
fmt.Println(<-table) // game over; grab the ball
fmt.Println(<-table) // game over; grab the ball
}
func player(name string, table chan *Ball) {
for {
ball := <-table
ball.hits++
fmt.Println(name, ball.hits)
time.Sleep(100 * time.Millisecond)
table <- ball
}
}
我遇到了僵局,真的不明白为什么。看看go例程中的最后一行,我尝试从第二行的第二行接收来自通道的值。在后台,两个goroutine仍然继续循环并相互发送值。对我来说似乎是一个表变量通道的多接收器。
我的主要问题是,第二个样本我遇到了什么僵局?
答案 0 :(得分:3)
在后台,两个goroutine仍然继续循环并相互发送值。
不,他们没有。
当您使用make(chan *Ball)
制作频道时,您正在制作无缓冲频道。这相当于说make(chan *Ball,0)
,这意味着通道可以容纳0个内容 - 或者更明确地说,对通道的任何写入都将阻塞,直到另一个例程从通道读取,反之亦然。
使用无缓冲通道执行的顺序是:
ball := <-table
的表中读取,阻止直到table
写入ball := <-table
的表中读取,阻止直到table
写入 Main使用以下行将球写入table
:
table <- new(Ball) // game on; toss the ball
这不会被阻止,因为有人正在等待阅读该频道。
ball := <-table
之后继续执行)table <- ball
,未阻止,因为乒乓球正在等待ball := <-table
之后继续执行)table <- ball
的桌面上,因为ping等待而未被阻止ball := <-table
之后继续执行)这是使用通道确保一次只运行一个例程的一个很好的例子。
要结束游戏,主线程只需在一秒钟后将球从通道中夺走:
time.Sleep(1 * time.Second)
fmt.Println(<-table) // game over; grab the ball
此后,table
频道将为空,并且其上的任何进一步读取都将被阻止。
player
例程都在ball := <- table
被阻止。如果您在主线程中进行了进一步的<-table
读取,那么 读取也会阻塞,直到例程尝试写入表通道。但是,由于没有其他例程正在运行,因此会出现死锁(所有goroutine都被阻止)。
确定。我可以在通道中放两个球吗?
没有。
如果我们尝试在通道中放置两个球,我们可能会得到输出:
Ping 1
Pong 1
因为两个player
例程都会被卡住,试图将他们的球放回频道 - 但是没有人会尝试阅读它。订单如下:
table
(未被阻止,因为有人在等待阅读)table
(未被阻止,因为有人在等待阅读)Here's a program to illustrate
正如评论者所指出的,结束游戏更好的办法是关闭频道。但是,我希望这次讨论能够消除你的困惑。