Go语言测验游戏双计时器异常:单次超时后每题均触发超时提示
解决Go语言测验游戏中本地计时器重复触发的问题
我看到你遇到的问题了——一旦某道题超时未答,后续哪怕按时完成题目,也会一直弹出“Times up for this question”的提示。这个问题的根源其实不是计时器的Stop或Reset方法用错了,而是多个读取输入的goroutine在争抢标准输入。
问题原因分析
在你的代码里,每道题都会启动一个新的goroutine调用fmt.Scanf等待用户输入。当某道题的本地计时器超时后,你调用了timerLocal.Stop(),但对应的goroutine并没有退出——它会一直卡在fmt.Scanf那里等待输入。当你后续为新题目输入答案时,这个输入会被之前未退出的goroutine抢走,当前题目对应的goroutine根本拿不到输入,只能等到本地计时器再次超时,于是就出现了重复提示的情况。
解决方案:单goroutine处理输入
解决这个问题的核心是让整个程序只有一个goroutine负责读取用户输入,避免多个goroutine争抢标准输入。我们可以用bufio.Scanner在单独的goroutine中持续读取输入,然后通过通道将输入传递给主循环。
修改后的代码
package main import ( "bufio" "fmt" "os" "time" ) // 假设problems的结构是这样的 type Problem struct { q string a string } func main() { // 这里模拟传入的参数,实际使用时替换为你的命令行参数 timeLimit := 30 // 全局时间限制(秒) timeQA := 10 // 每题时间限制(秒) problems := []Problem{ {"2+3", "5"}, {"10-4", "6"}, {"7*2", "14"}, } timerGlobal := time.NewTimer(time.Duration(timeLimit) * time.Second) correct := 0 // 启动单独的goroutine读取所有用户输入 inputChan := make(chan string) go func() { scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { inputChan <- scanner.Text() } // 输入出错或EOF时关闭通道 close(inputChan) }() problemLoop: for i, p := range problems { fmt.Printf("Problem #%d: %s = ", i+1, p.q) timerLocal := time.NewTimer(time.Duration(timeQA) * time.Second) select { case <-timerGlobal.C: fmt.Print("\nTimes up :(") timerLocal.Stop() // 清空剩余输入,让读取goroutine正常退出 for range inputChan { } break problemLoop case <-timerLocal.C: fmt.Println("\nTimes up for this question") timerLocal.Stop() // 如果用户在超时后输入了内容,跳过这个输入,避免影响下一题 select { case <-inputChan: default: } case answer, ok := <-inputChan: if !ok { // 输入通道关闭,提前结束测验 break problemLoop } if answer == p.a { correct++ } timerLocal.Stop() // 读取本地计时器可能已触发的信号,避免残留 select { case <-timerLocal.C: default: } } } fmt.Printf("\nYou scored %d out of %d.\n", correct, len(problems)) }
关键改进点
- 单输入goroutine:用一个goroutine处理所有输入,彻底解决多个goroutine抢输入的问题。
- 清理残留输入:当本地计时器超时后,检查并跳过用户可能已经输入的内容,确保下一题的输入不会被干扰。
- 处理计时器残留信号:在用户答对题目后,读取本地计时器通道中可能存在的触发信号,避免后续出现意外行为。
- 更健壮的输入处理:用
bufio.Scanner读取整行输入,比fmt.Scanf更符合用户输入习惯(支持带空格的答案,以及正确处理回车)。
内容的提问来源于stack exchange,提问作者Uday Yadav
相关产品推荐
相关产品推荐

