[БЕЗ_ЗВУКА] А теперь давайте поговорим о каналах. Основная трудность при многопроцессорной разработке, то есть когда ваша программа использует несколько ядер процессоров или даже несколько процессоров, состоит в том, что вам нужно как-то их синхронизировать между собой и каким-то образом передавать данные между разными потоками. В Go для этих целей есть каналы. Каналы нужны, для того чтобы передавать владения данными в другую горутину. Как это работает? Смотрите, для начала нужно создать какой-то канал, канал создается конструкцией make, после идет ключевое слово chan и тип канала. То есть каналы будут тоже типизированы, в них можно класть только определенный тип данных. После этого, смотрите, я запускаю горутину, которая принимает на вход канал (in — это имя канала, chan int — это тип канала), вычитывает оттуда какое-то значение. Вычитывание из канала происходит посредством стрелки слева от канала, и печатается значение. Это отдельная горутина, которую мы запустили. В основной горутине мы кладет значение в этот канал. Кладется значение в канал, используя стрелку справа от канала. И затем мы что-то печатаем. Теперь давайте запустим это и посмотрим, как оно работает. Смотрите: сначала мы положили значение в канал, после этого дела мы вывели текст из главной горутины, после программа ожидает ввода от меня строки, и управление переключилось в горутину. Горутина получила значение из канала, и после значения что-то вывела. Но есть нюанс. Смотрите, такая конструкция make (chan int), она делает небуферизированный канал. Что это значит? Это значит, что, когда я кладу в канал какое-то значение, кто-то с другой стороны этого канала должен его прочитать. И если никто не читает, то горутина блокируется, до тех пор пока на той стороне не появится тот, кто вычтет это значение. К чему это можем привести? Привести это может к тому, что мы залочимся. Эта ситуация называется deadlock — когда горутина ждет, но не может дождаться. Давайте воспроизведем эту ситуацию. Смотрите, напишем что-то еще в канал. [ШУМ] Мы написали второе значение. И давайте запустим. Все плохо — рантайм ругнулся на то, что мы залочились на строчке 15, на отправке в канал. Давайте посмотрим, что происходит. На строчке 15 мы пытаемся отправить еще значение в канал. Но горутина, в которую мы указали, чтобы вычитать это значение, она выполнилась только один раз и завершилась после этого дела. То есть наша главная горутина, функция main, повисла на строчке 15 в ожидании, пока кто-то вычитает значение из канала. И все — и произошел deadlock. В данном случае нам повезло, потому что наша программа, она завершилась сразу с ошибкой. Но в случае если в deadlock попадут какие-то другие горутины внутри вашей программы, а другие горутины будут продолжать работать, которые будут выполнять совершенно другую работу, то может прийти ситуация, которая называется утечка горутин, то есть горутина залочилась и чего-то ждет, но никогда не дождется. Это приводит к утечке памяти, то есть это весьма плохая ситуация, Теперь давайте рассмотрим, каким образом вообще это можно избежать. Вроде бы не очень удобно иметь небуферизированные каналы. Каналы в Go могут быть буферизированными. Что это значит? Это значит, что канал может принимать не одно значение, до того как он залочится, а несколько. Осуществляется это если мы укажем значение буфера для этого канала. Смотрите. После запятой в конструкции make я могу указать 0 — это по-прежнему небуферизированный канал. Либо могу указать значение 1. 1 это значит, что я могу положить одно значение в буфер и, если я пытаюсь положить туда второе значение, то только тогда я залочусь и буду ждать пока там освободится место. Теперь давайте запустим нашу программу и посмотрим, что получилось. Смотрите, наша программа успешно отработала. Ошибки нет, и мы ее успешно завершаем. Это происходит за счет того, что на строчке 15 теперь наша программа не заблокировалась, потому что в нашем канале есть место, куда мы можем положить значение, не блокируясь. А теперь давайте рассмотрим, как можно работать в цикле канала. Вот перед вами программа, которая запускается, принимает канал. В данном случае это небуферизированный канал. И начинает печатать туда значения. После закрывает канал, а в главной горутине у нас есть конструкция, у нас есть цикл, который, используя range, просто итерируется по каналу, по тем значениям, которые туда приходят. Давайте посмотрим, как это работает. [ШУМ] Смотрите, мы записали что-то в канал, теперь мы залочились, управление получила главная горутина, она прочитала тут значения, и пошло дальше. И после этого, в конце, после 4-й итерации наш генератор, он завершил работу, управление вернулось в главную горутину, и она вывела значение. Давайте посмотрим, как это работает. Мы принимаем канал, в Go можно указать, при передаче канала в функцию можно указать, какого типа будет этот канал. Например, если я укажу просто chan int, это значит, что я в канал смогу и писать, и читать. Если я поставлю стрелку, это значит, что я смогу с этого канала только читать. Если я поставлю стрелку справа, это значит, что я смогу в канал только писать. Это удобно, чтобы не делать те операции, которые не ожидаются от этой горутины. И такого рода вещи проверяются на уровне компиляции. Смотрите: пишем-пишем-пишем, потом закрываем канал. Наш цикл должен когда-то прерваться. Когда цикл прерывается? Как раз таки закрытие канала ведет к тому, что цикл завершается. Давайте попробуем убрать закрытие канала. Смотрите, я убрал закрытие канала, генератор закончил работу, мы получили последнее значение, и теперь опять впали в deadlock. Почему? Потому что в канал in уже больше никто не пишет, а мы на строчке 20 все еще ждем оттуда значений. То есть конструкция close, она позволяет завершить цикл, которые пытается итерироваться по значениям, идущим в канал.