Мы собираемся взглянуть на то, как вы передаете каналы по каналам в 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 будет «приводить» канал, чтобы поддерживать только указанное направление. Удобно.
До скорого.