Почему нет ошибки, что приемник заблокирован? - PullRequest
0 голосов
/ 15 января 2020

В соответствии с Go документацией :

Приемники всегда блокируются до тех пор, пока не будет данных для приема

Этот тест должен завершиться неудачей, поскольку для при последней операции приема из канала отсутствует соответствующая запись:

package main

import "fmt"

func main() {
    c := make(chan int)    
    for i := 0; i < 4; i++ { // 4 async reads
      go func() {
            fmt.Println("received:", <-c)
         }()

    }    
    // just 3 writes, 1 write is missing
    c <- 1   
    c <- 2 
    c <- 3   
}

Однако скрипт не завершается с сообщением об ошибке в процедуре чтения, но он успешно печатает 3 значения:

received: 1
received: 2
received: 3

Почему это так, или я неправильно понял что-то о синхронизации?

1 Ответ

4 голосов
/ 15 января 2020

Здесь нет тупиков, потому что программа main не заблокирована. Он отправляет 3 значения на c, что успешно, потому что есть 4 запущенных goroutines, получающих от него, и затем это заканчивается. И с этим ваше приложение также заканчивается, оно не ждет завершения других не-1003 * подпрограмм. См. Нет вывода из goroutine .

В результате тупиковой ситуации все goroutines должны быть заблокированы. Здесь дело обстоит иначе.

Это не ошибка, чтобы попытаться получить от канала, где нет никого (в настоящее время или когда-либо) готового к отправке. На самом деле это совершенно нормально. Это один из вариантов использования каналов: он выступает в качестве инструмента синхронизации, вы можете отправлять / получать, и операция будет блокироваться до тех пор, пока не будет готов другой конец.

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

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

func main() {
    defer func() {
        fmt.Println("Remaining goroutines:", runtime.NumGoroutine()-1) //-1 for main
    }()

    // your code
}

С этим дополнением вы получите:

received: 1
received: 2
received: 3
Remaining goroutines: 1

Если вы измените свой l oop, чтобы запустить 14 функций вместо 1 , output скажет, что осталось 11 подпрограмм.

Последнее примечание: поскольку в вашем приложении функция main() не ожидает завершения других подпрограмм, они могут оставаться активными в момент вызова отложенных функций. , так что они могут или не могут быть включены в число оставшихся goroutines. Если вы будете использовать, например, sync.WaitGroup, чтобы дождаться их окончания, то они точно не будут включены. См. Предотвращение завершения функции main () до завершения выполнения goroutines sh in Golang.

...