Найти тему

#3 Аксиомы каналов в Go

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

аксиомы каналов
аксиомы каналов

1. Отправка в канал или получение из канала блокирует горутину

Если отправить данные в канал, и нет другой горутины, готовой принять эти данные, то горутина заблокируется. Точно так же, если попытаться получить данные из канала и в нем нет данных, то получим deadlock (взаимную блокировку). Это позволяет организовать естественную синхронизацию между горутинами:

-2

2. Отправка в nil канал и получение из nil канала блокируется навсегда

zero-value для каналов — это nil. Если отправить значение в nil канал, то горутина будет заблокирована навсегда, так как никогда не будет другой горутины, которая могла бы прочитать из этого канала. Тоже самое для чтения из nil-канала:

-3

Отсюда вытекает особенность работы с nil каналами через select . Если есть select, в котором одна из ветвей пытается отправить или получить из nil канала, эта ветвь никогда не будет выбрана, даже если другие ветви не готовы. Для того, чтобы избежать такое поведение, используем default.

-4

3. Закрытие nil канала приведет к панике

Закрытие nil канала не имеет осмысленного действия в контексте программы. Если бы оно было разрешено и не вызывало паники, это привело к неясному и потенциально ошибочному коду. Паника действует как явное напоминание разработчику о том, что произошло нечто непредвиденное:

-5

4. Закрытие уже закрытого канала приведет к панике

Закрытие канала является четким сигналом о том, что отправка данных завершилась. Повторное его закрытие не добавляет никакой новой информации и может указывать на ошибку в коде, все как в случае с nil каналами.

-6

5. Чтение из закрытого канала возвращает zero-value

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

-7

При первой попытке чтения (value1, ok := <-ch), горутина успешно получает значение 0 из канала. Так как это действительное значение, отправленное в канал, ok равно true. Будет выполнен код в блоке else — "received value: 0".

При второй попытке чтения (value2, ok := <-ch), горутина пытается прочитать из канала снова. Но так как канал уже был закрыт и в нем нет значений, то value2 будет нулевым значением для int (т.е. 0), и ok будет равно false. Это указывает на то, что канал закрыт и больше не содержит значений для чтения. Будет выведено "channel is closed!".

6. Запись в закрытый канал приведет к панике

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

-8

На этом все. В следующих постах рассмотрим шаблоны синхронизации горутин через каналы.