douh9817 2018-06-10 23:06
浏览 39
已采纳

如何在Golang中使用goroutines从stdin读取?

There is list of questions. I show question one after one to user and wait for user`s answer. Every question should be answered in some seconds( for example 5 second for question). If question is answered right and in time, then user get some points. My code is looks like:

 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
", &u)
            ans <- u
        }()

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

Problem is next: if user don`t answer to question, then he get message "Time is over", as expected. But next answer will not be processed, and user should type it again. It looks like next output:

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

User did not answer to question #3, so he need to answer to question #4 twice. I understand that problem is because goroutines and channels. But I don`t understand, why was not value, that was read from stdin after timeout, sent to or get from channel "ans".

Why value from channel was not properly received after timeout? How I can rewrite code, so user don`t need to repeat input twice after timeout to previous question?

Sorry for bad english and thanks for help.

  • 写回答

1条回答 默认 最新

  • doufang7385 2018-06-11 00:23
    关注

    What's going on here is that when you time out you still have a fmt.Scanf going in the previous goroutine. You're also allocating a new channel each loop. The end result means the scan from question 3 gets your first input of 4 and then tries to push it onto a channel that will never be read from. The second time you enter 4 it is read by the new goroutine and is then pushed onto the channel you're expecting to find the user's input on.

    I'd suggest, instead, that you offload the user input into a single goroutine that feeds a single channel.

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

    And then process your questions like so:

    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("
     Time is over!")
            }
        }
    }
    

    You would probably want to add some additional logic or handling to terminate the input reading goroutine at some point, depending on the actual life cycle of your program but that's a different problem.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 kafka 分区副本增加会导致消息丢失或者不可用吗?
  • ¥15 微信公众号自制会员卡没有收款渠道啊
  • ¥15 stable diffusion
  • ¥100 Jenkins自动化部署—悬赏100元
  • ¥15 关于#python#的问题:求帮写python代码
  • ¥20 MATLAB画图图形出现上下震荡的线条
  • ¥15 关于#windows#的问题:怎么用WIN 11系统的电脑 克隆WIN NT3.51-4.0系统的硬盘
  • ¥15 perl MISA分析p3_in脚本出错
  • ¥15 k8s部署jupyterlab,jupyterlab保存不了文件
  • ¥15 ubuntu虚拟机打包apk错误