Подтвердить что ты не робот

Какова точка односторонних каналов в Go?

Я изучаю Go и до сих пор очень впечатлен этим. Я прочитал все онлайн-документы на golang.org и нахожусь на полпути через Chrisnall "Разговорник по программированию на языке программирования Go". Я получаю понятие каналов и думаю, что они будут чрезвычайно полезны. Тем не менее, я, должно быть, пропустил что-то важное на этом пути, поскольку я не вижу смысла в односторонних каналах.

Если я правильно их интерпретирую, канал только для чтения может быть принят только, и канал только для записи может быть передан только, так почему у вас есть канал, на который вы можете отправлять и не получать? Могут ли они быть брошены из одного "направления" в другое? Если так, опять же, какой смысл, если нет фактического ограничения? Это не что иное, как намек на клиентский код цели канала?

4b9b3361

Ответ 1

Канал только для чтения может быть доступен только для чтения тем, кто его получает, тогда как отправитель все еще имеет двусторонний канал, на котором они могут писать. Например:

func F() <-chan int {
    // Create a regular, two-way channel.
    c := make(chan int)

    go func() {
        defer close(c)

        // Do stuff
        c <- 123
    }()

    // Returning it, implicitely converts it to read-only,
    // as per the function return value.
    return c
}

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

Ответ 2

Я думаю, что основной мотивацией для каналов только для чтения является предотвращение коррупции и паники канала. Представьте, что вы могли бы написать канал, возвращенный time.After. Это может испортить много кода.

Кроме того, возможны паники, если вы:

  • закрыть канал более одного раза
  • записать в закрытый канал

Эти операции - это ошибки времени компиляции для каналов только для чтения, но они могут вызывать неприятные условия гонки, когда несколько go-процедур могут записывать/закрывать канал.

Один из способов обойти это - никогда не закрывать каналы и не собирать мусор. Тем не менее, close предназначен не только для очистки, но и на самом деле имеет смысл использовать канал:

func consumeAll(c <-chan bool) {
    for b := range c {
        ...
    }
}

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

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

Edit:

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

func produce(l *net.TCPListener, c chan<- net.Conn) {
    for {
        conn, _ := l.Accept()
        c<-conn
    }
}

func consume(c <-chan net.Conn) {
    for conn := range c {
        // do something with conn
    }
}

func main() {
    c := make(chan net.Conn, 10)
    for i := 0; i < 10; i++ {
        go consume(c)
    }

    addr := net.TCPAddr{net.ParseIP("127.0.0.1"), 3000}
    l, _ := net.ListenTCP("tcp", &addr)
    produce(l, c)
}

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

Ответ 3

Каналы Go моделируются на Hoare, сообщающем последовательные процессы, алгебру процессов для concurrency, которая ориентирована на потоки событий между сообщающимися участниками (малыми "a" ). Таким образом, каналы имеют направление, потому что у них есть конец отправки и конец приема, то есть производитель событий и потребитель событий. Аналогичная модель используется также в Оккаме и Лимбо.

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