Какова точка односторонних каналов в Go?
Я изучаю Go и до сих пор очень впечатлен этим. Я прочитал все онлайн-документы на golang.org и нахожусь на полпути через Chrisnall "Разговорник по программированию на языке программирования Go". Я получаю понятие каналов и думаю, что они будут чрезвычайно полезны. Тем не менее, я, должно быть, пропустил что-то важное на этом пути, поскольку я не вижу смысла в односторонних каналах.
Если я правильно их интерпретирую, канал только для чтения может быть принят только, и канал только для записи может быть передан только, так почему у вас есть канал, на который вы можете отправлять и не получать? Могут ли они быть брошены из одного "направления" в другое? Если так, опять же, какой смысл, если нет фактического ограничения? Это не что иное, как намек на клиентский код цели канала?
Ответы
Ответ 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" ). Таким образом, каналы имеют направление, потому что у них есть конец отправки и конец приема, то есть производитель событий и потребитель событий. Аналогичная модель используется также в Оккаме и Лимбо.
Это важно - было бы трудно рассуждать о проблемах взаимоблокировки, если бы конец канала мог произвольно повторно использоваться как отправитель, так и получатель в разное время.