#20.3 golang Закрытие каналов - close() range()
Primary tabs
Forums:
Закрывать каналы следует, когда отправитель данных завершил свою работу и больше не планирует отправлять значения.
Для закрытия канала используется вызов:
close(имяКанала)
Если количество данных, которое должно попасть в канал получателю неизвестно, то закрытие канала служит сигналом для получателя, что можно переставать читать, например:
package main
import (
"fmt"
)
// источник данных
func producer(ch chan<- int) {
defer close(ch) // закрываем при завершении
for i := 0; i < 5; i++ {
ch <- i // пишем в канал
}
}
// получатель
func consumer(ch <-chan int) {
// цикл завершится, когда канал закроют
for num := range ch {
fmt.Println(num)
}
}
func main() {
ch := make(chan int)
go producer(ch)
consumer(ch)
}
-- тут мы:
- пользуем отложенный вызов для закрытия канала
- перебор в цикле через range - для канала признаком завершения перебра будет именно состояние его закрытия
- аргументы функций приемника и передачика данных объявлены как однонаправленные каналы
Незакрытый канал для примера выше заблокирует горутину-получатель (consumer) и будет впустую использовать ресурсы вашей оперативной памяти.
Где правильно закрывать канал
Закрывай канал в реальном коде (если это не демонстрационный пример) следует только в горутине которая передает в него данные (producer), никогда не пытайся закрывать канал в горутине-получателе (consumer).
Иначе пишуший, код может продолжить передавать данные в канал и это вызовет ошибку (panic).
Пример кода, который иллюстрирует обработку паники в ситуации, когда канал закрыт, но в него пытаются записать значение:
package main
import (
"fmt"
)
// Обработчик паник
func panicHandler() {
// Проверим была ли ошибка
if err := recover(); err != nil {
// если была, то обработаем:
fmt.Println(
"Мы упали c сообщением: \n",
err)
}
}
func main() {
defer panicHandler() // отложенная функция восстановления
ch := make(chan int)
close(ch)
// пишем в уже закрытый канал
ch <- 22 // Паника!
}
-- если запустим, то получим распечатку:
Мы упали c сообщением: send on closed channel
Проверяем закрыт ли канал
Проверить закрыт ли канал (если вы не используете range, как в примере выше) в явной форме можно при попытке прочесть из него данные, операция чтения на самом деле всегда возвращает два значения:
- очередного значение из канала (или значение по умолчанию для данного типа данных, если канал закрыт)
- и вторым, опциональным для чтения параметром, bool-ответ (false при закрытом канале)
Посмотрим пример:
import (
"fmt"
)
func main() {
ch := make(chan int)
// Закрываем канал до начала чтения
close(ch)
// пробуем считать значение
// и признак открытости канала
val, ok := <-ch
fmt.Printf("Значение: %v, Канал открытыт?: %v\n",
val,
ok)
}
- Log in to post comments
- 81 reads