最近发现, golang写的游戏服务器, 在非调试状态下, 一切正常, 但是在挂接gdb调试时, 无法收到网络消息. 打了很多日志, 发现, 只要有goroutine的地方, 都没有切换进入.
回想了下, goroutine的调度规则: 1.4之前, 在碰到syscall时, goroutine会被调度并处理. 1.4后, 只要有函数调用时, 均会进行一次调度. 密度比以前增加了, 更加接近真线程的处理.
根据这个原理, 问题应该出现在服务器底层没有给系统提供调度机会的点上. 我们的服务器通过一个bool型的chan进行阻塞, 让服务器维持阻塞进行消息处理不退出. 但是最近为了在windows下提供命令行支持, 增加了一些代码, 如下
1: func WaitForExit() {
2:
3: if len(peerMap) == 0 {
4: log.Println("no peer running, exit!")
5: return
6: }
7:
8: // 命令行功能只在windows下启用
9: if runtime.GOOS == "windows" {
10: reader := bufio.NewReader(os.Stdin)
11:
12: var running bool = true
13:
14: go func() {
15: select {
16: case <-exitChan:
17: running = false
18: }
19: }()
20:
21: for running {
22: data, _, _ := reader.ReadLine()
23: command := string(data)
24:
25: dispatchConsoleCommand(command)
26: }
27: } else {
28: // Linux环境
29: <-exitChan
30: }
31:
32: }
我暂时屏蔽了新加的这套功能, 维持<-exitChan, 问题马上解决
结合前面的猜测, 我估计在reader.ReadLine()函数内, 没有给底层提供调度的机会, 导致其他goroutine无法运行, 造成服务器卡死
技术讨论群: 309800774 欢迎golang爱好者加入, 纯技术研讨