如何在Golang中使用goroutine读取stdin?

时间:2018-06-10 23:06:58

标签: go concurrency goroutine

有问题清单。我一个接一个地向用户显示问题并等待用户的回答。每个问题都应该在几秒钟内回答(例如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和渠道。但是我不明白,为什么不是值,这是在超时后从stdin读取,发送到或来自通道“ans”。

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

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

1 个答案:

答案 0 :(得分:3)

这里发生的事情是,当你超时的时候,你仍然会在之前的goroutine中找到die(X::Price::PriceInvalid.new(:$price));。您还要为每个循环分配一个新通道。最终结果意味着来自问题3的扫描首先输入4,然后尝试将其推送到永远不会被读取的通道上。第二次输入4时,它会被新的goroutine读取,然后被推到您期望找到用户输入的频道上。

相反,我建议你将用户输入卸载到一个提供单个频道的goroutine中。

fmt.Scanf

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

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

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