【问题标题】:How to read from stdin with goroutines in Golang?如何使用 Golang 中的 goroutine 从标准输入读取?
【发布时间】:2018-06-10 23:06:58
【问题描述】:

有问题列表。我一个接一个地向用户展示问题并等待用户的回答。每个问题都应该在几秒钟内回答(例如问题 5 秒)。如果问题得到正确及时的回答,那么用户就会得到一些分数。 我的代码是这样的:

 for i := 0; i < len(questions); i++ {
        fmt.Println(questions[i].Text)
        ans := make(chan int)
        go func() {
            fmt.Print("Enter answer ")
            var u int
            fmt.Scanf("%d\n", &u)
            ans <- u
        }()

        select {
        case userAnswer := <-ans:
            if userAnswer == questions[i].Answer {
                points++
            }
        case <-time.After(5 * time.Second):
            fmt.Println("\n Time is over!")
        }
    }

接下来的问题是:如果用户不回答问题,那么他会收到消息“时间结束”,正如预期的那样。但是下一个答案将不会被处理,用户应该再次输入。看起来像下一个输出:

question with answer  1
Enter answer: 1
1  is right answer
question with answer  2
Enter answer: 2
2  is right answer
question with answer  3
Enter answer: 
 Time is over!
question with answer  4
Enter answer: 4
4
4  is right answer
question with answer  5
Enter answer: 5
5  is right answer

用户没有回答问题 #3,因此他需要回答问题 #4 两次。 我知道这个问题是因为 goroutines 和通道。但我不明白,为什么不是值,超时后从标准输入读取,发送到通道“ans”或从通道“ans”获取。

为什么超时后没有正确接收来自通道的值?如何重写代码,让用户在上一个问题超时后不需要重复输入两次?

抱歉英语不好,感谢您的帮助。

【问题讨论】:

    标签: go concurrency goroutine


    【解决方案1】:

    这里发生的情况是,当您超时时,您仍然有一个 fmt.Scanf 进入前一个 goroutine。您还为每个循环分配一个新通道。最终结果意味着来自问题 3 的扫描获得了您的第一个输入 4,然后尝试将其推送到永远不会被读取的通道上。第二次输入 4 时,新的 goroutine 会读取它,然后将其推送到您希望找到用户输入的通道上。

    相反,我建议您将用户输入卸载到单个 goroutine 中,该 goroutine 为单个通道提供数据。

    func readInput(input chan<- int) {
        for {
            var u int
            _, err := fmt.Scanf("%d\n", &u)
            if err != nil {
                panic(err)
            }
            input <- u
        }
    }
    

    然后像这样处理你的问题:

    func main() {
        var points int
        userInput := make(chan int)
    
        go readInput(userInput)
    
        for i := 0; i < len(questions); i++ {
            fmt.Println(questions[i].Text)
            fmt.Print("Enter answer ")
    
            select {
            case userAnswer := <-userInput:
                if userAnswer == questions[i].Answer {
                    fmt.Println("Correct answer:", userAnswer)
                    points++
                } else {
                    fmt.Println("Wrong answer")
                }
            case <-time.After(5 * time.Second):
                fmt.Println("\n Time is over!")
            }
        }
    }
    

    您可能希望添加一些额外的逻辑或处理来在某个时候终止输入读取 goroutine,具体取决于程序的实际生命周期,但这是一个不同的问题。

    【讨论】: