Код: ch := make(chan int) go func(){ ch <- 1 }() <-ch <-ch Анализ поведения и блокировки - Канал создан как небуферизованный: make(chan int) — отправка блокируется до тех пор, пока кто‑то не примет значение. - Горутинa делает одно отправление 111. Оно завершится только когда main выполнит одно чтение <−ch<-ch<−ch. - Первое чтение принимает это значение — отправившая горутина возвращается и завершается. - Второе чтение <−ch<-ch<−ch ждёт ещё одного отправления, которого не будет. В итоге все горутины блокируются — runtime выдаст panic: fatal error: all goroutines are asleep - deadlock! Правильные способы организовать завершение/согласование 1) Принимать ровно столько раз, сколько отправляют. Если отправляется одно значение — делайте одно чтение. 2) Использовать закрытие канала, если нужно сигнализировать "больше значений не будет": - отправляющая сторона закрывает канал: go func(){ ch <- 1; close(ch) }() - прием можно делать в цикле: for v := range ch { /* v */ } - после закрытия чтение возвращает нулевое значение и ok==false: v, ok := <-ch; if !ok { /* канал закрыт */ } 3) Использовать сигнальный канал или sync.WaitGroup для ожидания завершения горутин: - done := make(chan struct{}) go func(){ ch <- 1; close(done) }() <-done // дождаться завершения - или var wg sync.WaitGroup wg.Add(1) go func(){ defer wg.Done(); ch <- 1 }() v := <-ch wg.Wait() 4) Буферизованный канал, если нужно, чтобы отправитель не блокировался до немедленного приёма: - ch := make(chan int, 111) go func(){ ch <- 1 }() v := <-ch // если читать больше раз, чем было отправлено, всё равно будет блокировка 5) Неблокирующие операции с select (если прием необязателен): - select { case v := <-ch: /* получили v */ default: /* не блокироваться */ } Замечания/подсказки - Закрывать канал должен только отправитель(и). Закрытие канала из нескольких мест или закрытие уже закрытого вызовет panic. - Если вы ожидаете одноразовый сигнал о завершении — используйте chan struct{} или WaitGroup. - Если нужно передавать набор значений одного производителя — закрывайте канал после всех отправлений и перебирайте через range. Краткий исправленный пример (вариант с закрытием): go func(){ ch <- 1 close(ch) }() for v := range ch { _ = v }
ch := make(chan int)
go func(){ ch <- 1 }()
<-ch
<-ch
Анализ поведения и блокировки
- Канал создан как небуферизованный: make(chan int) — отправка блокируется до тех пор, пока кто‑то не примет значение.
- Горутинa делает одно отправление 111. Оно завершится только когда main выполнит одно чтение <−ch<-ch<−ch.
- Первое чтение принимает это значение — отправившая горутина возвращается и завершается.
- Второе чтение <−ch<-ch<−ch ждёт ещё одного отправления, которого не будет. В итоге все горутины блокируются — runtime выдаст panic: fatal error: all goroutines are asleep - deadlock!
Правильные способы организовать завершение/согласование
1) Принимать ровно столько раз, сколько отправляют. Если отправляется одно значение — делайте одно чтение.
2) Использовать закрытие канала, если нужно сигнализировать "больше значений не будет":
- отправляющая сторона закрывает канал:
go func(){ ch <- 1; close(ch) }()
- прием можно делать в цикле:
for v := range ch { /* v */ }
- после закрытия чтение возвращает нулевое значение и ok==false:
v, ok := <-ch; if !ok { /* канал закрыт */ }
3) Использовать сигнальный канал или sync.WaitGroup для ожидания завершения горутин:
- done := make(chan struct{})
go func(){ ch <- 1; close(done) }()
<-done // дождаться завершения
- или
var wg sync.WaitGroup
wg.Add(1)
go func(){ defer wg.Done(); ch <- 1 }()
v := <-ch
wg.Wait()
4) Буферизованный канал, если нужно, чтобы отправитель не блокировался до немедленного приёма:
- ch := make(chan int, 111)
go func(){ ch <- 1 }()
v := <-ch
// если читать больше раз, чем было отправлено, всё равно будет блокировка
5) Неблокирующие операции с select (если прием необязателен):
- select {
case v := <-ch: /* получили v */
default: /* не блокироваться */
}
Замечания/подсказки
- Закрывать канал должен только отправитель(и). Закрытие канала из нескольких мест или закрытие уже закрытого вызовет panic.
- Если вы ожидаете одноразовый сигнал о завершении — используйте chan struct{} или WaitGroup.
- Если нужно передавать набор значений одного производителя — закрывайте канал после всех отправлений и перебирайте через range.
Краткий исправленный пример (вариант с закрытием):
go func(){
ch <- 1
close(ch)
}()
for v := range ch {
_ = v
}