Если горутина записывает в небуферизованный канал и никакая другая горутина не читает из этого канала - тогда запись будет заблокирована навсегда. Это вызовет утечку горутины. Это то, что вы испытываете.
Если у вас есть горутина «производителя», которая записывает в канал, вам нужен способ дать ей сигнал остановиться. Закрытие канала здесь не критично - каналы собираются мусором, когда они go выходят за рамки. Заблокированная горутина (которая никогда не разблокируется) считается утечкой, поскольку она никогда не будет восстановлена, поэтому вам действительно нужно, чтобы горутина завершилась.
Вы можете сигнализировать о намерении выйти разными способами - два самых популярных будучи:
Сигнал: готовый канал
func iterator(n int, c chan int, done <-chan struct{}) {
for i := 0; i < n; i++ {
select {
case c <- i:
case <-done:
break
}
}
close(c)
fmt.Println("iterator End")
}
читатель goroutine:
c := make(chan int)
done := make(chan struct{})
go iterator(5, c, done)
for i := range c {
if i == 2 {
break
}
fmt.Println(i)
}
close(done) // signal writer goroutine to quit
Сигнал: context.Context
func iterator(ctx context.Context, n int, c chan int) {
defer close(c)
defer fmt.Println("iterator End")
for i := 0; i < n; i++ {
select {
case c <- i:
case <-ctx.Done():
fmt.Println("canceled. Reason:", ctx.Err())
return
}
}
}
читать горутину:
func run(ctx context.Context) {
ctx, cancel := context.WithCancel(ctx)
defer cancel() // call this regardless - avoid context leaks - but signals producer your intent to stop
c := make(chan int)
go iterator(ctx, 5, c)
for i := range c {
if i == 2 {
break
}
fmt.Println(i)
}
}
https://play.golang.org/p/4-fDyCurB7t