You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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))
}

关键改进点

  1. 单输入goroutine:用一个goroutine处理所有输入,彻底解决多个goroutine抢输入的问题。
  2. 清理残留输入:当本地计时器超时后,检查并跳过用户可能已经输入的内容,确保下一题的输入不会被干扰。
  3. 处理计时器残留信号:在用户答对题目后,读取本地计时器通道中可能存在的触发信号,避免后续出现意外行为。
  4. 更健壮的输入处理:用bufio.Scanner读取整行输入,比fmt.Scanf更符合用户输入习惯(支持带空格的答案,以及正确处理回车)。

内容的提问来源于stack exchange,提问作者Uday Yadav

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 16:57:59