Go语言channel阻塞关闭实验
在游戏中有一个常见的场景:玩家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进程接收消息,该如何处理呢?留个疑问大家自己思考吧 :)