Нередко одна горутина транслирует другой горутине через канал не одиночные значения, а некоторый поток данных. В этом случае общий алгоритм состоит в том, что горутина-отправитель в течение некоторого периода отправляет данные. Когда данные для отправки закончились, работа сделала, отправитель закрывает канал.
Горутина-получатель в бесконечном цикле получает данные из канала. Если будет получен маркер закрытия канала, то осуществляется выход из бесконечного цикла.
package main import "fmt" func main(){ intCh := make(chan int) go factorial(7, intCh) for { num, opened := <- intCh // получаем данные из потока if !opened { break // если поток закрыт, выход из цикла } fmt.Println(num) } } func factorial(n int, ch chan int){ defer close(ch) result := 1 for i := 1; i <= n; i++{ result *= i ch <- result // посылаем по числу } }
В данном случае функция main и горутина factorial взаимодействуют через канал intCh. Функция factorial последовательно вычисляем факториалы чисел от 1 до n.
И все вычисленные значения передаются в канал. По завершении функции factorial канал закрывается вызывом defer close(ch)
.
В функции main в бесконечом цикле отправленные данные извлекаются из канала. При этом также проверяется, открыт ли канал. И ели вдруг канал закрыт и соответственно нет смысла получать из него данные, происходит выход из бесконечного цикла:
for { num, opened := <- intCh if !opened { break } fmt.Println(num) }
Консольный вывод программы:
1 2 6 24 120 720 5040
При извлечении значений из канала мы можем использовать ту же форму цикла for, которая применяется для перебора коллекций:
for переменная := канал{ //........... }
Например, перепишем предыдущий пример:
package main import "fmt" func main(){ intCh := make(chan int) go factorial(7, intCh) for num := range intCh{ fmt.Println(num) } } func factorial(n int, ch chan int){ defer close(ch) result := 1 for i := 1; i <= n; i++{ result *= i ch <- result } }
Когда канал будет закрыт, то автоматически произойдет выход из цикла for.