在游戏中有一个常见的场景:玩家A的进程向玩家B的进程发送消息,以完成一次互动
使用Go语言实现上面这个场景,有可能出现一种异常情况:玩家A像玩家B的一个特定channel发送消息,并阻塞等待玩家B接收,但是玩家B进程正在执行一次tcp发送,这时候tcp发送失败,玩家B被系统判定成掉线,进而玩家B进程退出了,这时候玩家B的channel也将随之关闭
这里就会有一个疑问,玩家A正在等待channel被接收,但是玩家B把这个channel关闭了,将会发生什么呢?
这里有一个简化以上模型的实验代码:
package main
import "fmt"
func main() {
a := make(chan int)
b := make(chan int)
go func(a, b chan int) {
fmt.Println("A wait")
a <- 1
fmt.Println("A exit")
b <- 1
}(a, b)
go func(a chan int) {
fmt.Println("B exit")
close(a)
}(a)
<- b
fmt.Println("C exit")
}
以上实验代码模拟两个Go进程,进程A堵塞在发送消息,B进程启动后就把channel关闭。
运行以上代码将得到以下结果:
A wait
B exit
panic: runtime error: send on closed channel
goroutine 2 [running]:
main._func_001(0xf840001910, 0xf8400018c0, 0x0, 0x0)
/tmpfs/gosandbox-4b95d6cd_c5d5dd69_106d64b3_e851ad9c_c4476823/prog.go:11 +0xaa
created by main.main
/tmpfs/gosandbox-4b95d6cd_c5d5dd69_106d64b3_e851ad9c_c4476823/prog.go:14 +0x74
goroutine 1 [chan receive]:
main.main()
/tmpfs/gosandbox-4b95d6cd_c5d5dd69_106d64b3_e851ad9c_c4476823/prog.go:21 +0xa4
上面的实验说明channel被关闭将导致发送端异常。那么如何满足最前面我们提出的需求呢?
下面是改进后的代码:
package main
import "fmt"
func main() {
a := make(chan int)
b := make(chan int)
c := make(chan int)
go func(a, b, c chan int) {
fmt.Println("A wait")
select {
case b <- 1:
fmt.Println("This will never happen")
case <- a:
fmt.Println("A knows B exit")
}
fmt.Println("A exit")
c <- 1
}(a, b, c)
go func(a, b chan int) {
fmt.Println("B exit")
close(b)
a <- 1
}(a, b)
<- c
fmt.Println("C exit")
}
第二段实验代码中,加入了另外一个channel,用以在B进程退出时发送退出通知,这时候再运行代码将得到以下结果。
A wait
B exit
A knows B exit
A exit
C exit
看起来好像已经可以满足需求了?但是还有个问题,B进程发送退出通知到channel里的时候,只能由一个进程先接收到,如果此时是多个进程都在等待B进程接收消息,该如何处理呢?留个疑问大家自己思考吧 :)
原标签已从 2012-05-15 存档快照 恢复。本文按原站快照恢复排版,并接回已核实的本地图片。查看恢复说明