Go Channels — нажатие на канал останавливает выполнение

Я пытаюсь создать викторину командной строки, в которой пользователям будет задаваться вопрос за вопросом, пока они либо не ответят на последний вопрос, либо не истечет время ожидания.

Я хотел использовать каналы, чтобы научиться правильно их использовать, и я, так сказать, наткнулся на блокировщик.

Идея состоит в том, чтобы correctAnswersCh начинался с 0, и после каждого правильного ответа он увеличивался на 1.

Тест всегда останавливается на строке 3 функции quiz() после того, как я ставлю ноль в канал.

Я добавил немного кода ниже, но полный код находится здесь: https://play.golang.org/p/vzRCTc7MpIK

func main() {
    questions, err := getCsvData()
    var limit = time.Duration(3)


    flag.Parse()

    if err != nil {
        log.Fatal(err)
    }

    quizComplete := make(chan bool)
    correctAnswersCh := make(chan int)
    go quiz(quizComplete, questions, correctAnswersCh)

    select {
    case <-time.After(limit*time.Second):
        fmt.Println("Timed Out")
    }
    fmt.Printf("Correct Answers: %v\n", <-correctAnswersCh)
}

func quiz(quizComplete chan bool, questions [][]string, correctAnswersCh chan int) {
    reader := bufio.NewReader(os.Stdin)
    correctAnswersCh <- 0
    // execution stops here.  0 is added to correctAnswersCh, then the quiz func stops
    for _, question := range questions {

        fmt.Print(question[0], "= ")
        answer, _ := reader.ReadString('\n')

        if strings.TrimSpace(answer) == question[1] {
            cA := <-correctAnswersCh
            cA++
            correctAnswersCh <- cA
        }
    }
    quizComplete <- true
}

person user1894292    schedule 18.11.2019    source источник
comment
Канал блокируется при добавлении нуля, потому что канал не буферизован. Даже если вы запишете в него значение, оно будет заблокировано в этой строке до тех пор, пока канал не будет прочитан где-то еще.   -  person Emin Laletovic    schedule 18.11.2019


Ответы (1)


Ваш канал correctAnswersCh не буферизован, поэтому отправка на него чего-либо блокируется до тех пор, пока кто-то не получит от него сообщение. И поскольку ваша функция main() получает от нее только после истечения времени ожидания, ваше приложение до этого времени заблокировано.

Один простой обходной путь — дать 1 буфер каналу:

correctAnswersCh := make(chan int, 1)

Хотя это какое-то странное использование каналов. Если вы намерены создать безопасный счетчик параллелизма, используйте счетчик aotmic, например. atomic.AddInt32(). Другой вариант — использовать мьютекс (sync.Mutex или sync.RWMutex) для защиты ресурса (переменной) при одновременном доступе из нескольких горутин.

person icza    schedule 18.11.2019