Мы собираемся взглянуть на то, как вы передаете каналы по каналам в Go, и воспользуетесь преимуществами предлагаемой безопасности типов. Давайте сразу перейдем к коду:

У нас есть два достаточно простых воркера, которым нужен канал для работы. Каждый работник, по сути, «сосчитает» до десяти, а затем закончит. Здесь нет ничего особенного. давайте перейдем к функции main():

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

Мы помещаем функцию iCanRead() в Go Routine, чтобы оставить ее в фоновом режиме, пока мы заполняем канал, который она слушает. Заполнение dataChannel выполняется функцией iCanWrite().

Простые вещи. Однако здесь есть право на ошибку.

Веселье, беги, ошибка времени

Посмотрите, что происходит, когда мы пишем в канал из функции iCanRead():

Это компилируется, но когда мы выполняем, мы получаем время выполнения panic(): fatal error: all goroutines are asleep — deadlock! Yikes! Это связано с тем, что мы поместили другое сообщение в канал, и наш читатель выйдет, прежде чем получит возможность его прочитать, следовательно: тупик.

Как решить эту проблему? Мы используем безопасность типов Go, которую он предлагает нам как с каналами, так и с другими типами. Давайте изменим наших воркеров, чтобы они могли только читать или писать соответственно:

Если бы мы оставили запись в канал в iCanRead(), то получили бы ошибку компиляции: invalid operation: inbound <- message (send to receive-only type <-chan uint) — наш ридер может только читать, но мы пытаемся писать в односторонний канал, следовательно, мы не можем скомпилировать и воспользуемся преимуществом безопасности типов Go. Мы узнаем об этой ошибке во время компиляции, а не во время выполнения, что может быстро превратиться в «забавное время».

Создать канал только для чтения/записи

Мы не можем создать канал только для чтения в main(), потому что тогда мы не сможем передать его нашему воркеру iCanWrite():

А если мы создадим канал только для записи? cannot use dataChannel (type chan<- uint) as type <-chan uint in argument to iCanRead — как и ожидалось. Поэтому для начала мы должны создать двунаправленный канал для чтения/записи, особенно если мы хотим поделиться им так, как мы предлагаем здесь.

tl;dr

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

Создав канал чтения/записи и передав его функциям, которые ожидают только односторонний канал, Go будет «приводить» канал, чтобы поддерживать только указанное направление. Удобно.

До скорого.