Как читать с stdin с goroutines на Голанге? - PullRequest
0 голосов
/ 11 июня 2018

Есть список вопросов.Я показываю пользователю вопрос один за другим и жду ответа пользователя.Каждый вопрос должен быть дан ответ за несколько секунд (например, 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 дважды.Я понимаю, что проблема в том, что горутины и каналы.Но я не понимаю, почему не было значения, которое было прочитано из stdin после тайм-аута, отправлено или получено из канала "ans".

Почему значение из канала не было правильно получено после тайм-аута?Как я могу переписать код, чтобы пользователю не нужно было повторять ввод дважды после истечения времени ожидания предыдущего вопроса?

Извините за плохой английский и спасибо за помощь.

1 Ответ

0 голосов
/ 11 июня 2018

Что здесь происходит, так это то, что, когда вы выходите из игры, у вас все еще есть fmt.Scanf, идущий в предыдущей программе.Вы также выделяете новый канал для каждого цикла.Конечный результат означает, что при сканировании из вопроса 3 ваш первый ввод равен 4, а затем пытается выдвинуть его на канал, с которого никогда не будет считываться.Во второй раз, когда вы вводите 4, он читается новой программой и затем выдвигается на канал, на котором вы ожидаете найти ввод пользователя.

Вместо этого я бы предложил выгрузить ввод пользователяв одну процедуру, которая питает один канал.

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

Возможно, вы захотите добавить дополнительную логику или обработку для завершенияв некоторый момент вводит чтение программы, в зависимости от фактического жизненного цикла вашей программы, но это другая проблема.

...