0Pricing
Go Academy · Урок

Небуферизованные каналы

Синхронная передача данных между горутинами

«Небуферизованные каналы» — бесплатный урок Go Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Go Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Go Academy содержит 4 уроков всего.

Что такое канал

Канал — это типизированный способ обмена данными между goroutine. Каналы следуют принципу Go: Не обменивайтесь данными через общую память; используйте общую память через обмен данными.

package main
import "fmt"

func main() {
    ch := make(chan int)   // unbuffered int channel

    go func() {
        ch <- 42  // send: blocks until someone receives
    }()

    v := <-ch  // receive: blocks until someone sends
    fmt.Println(v) // 42
}

Семантика небуферизованного канала

Небуферизованный канал не имеет ёмкости. Отправка блокируется, пока не будет готов получатель, а получение блокируется, пока отправитель не отправит значение. Это обеспечивает гарантированную синхронную передачу:

package main
import "fmt"

func double(in <-chan int, out chan<- int) {
    for v := range in {
        out <- v * 2
    }
    close(out)
}

func main() {
    in := make(chan int)
    out := make(chan int)
    go double(in, out)
    go func() { in <- 3; in <- 5; close(in) }()
    for v := range out { fmt.Println(v) }
    // 6
    // 10
}

Канал как средство синхронизации

Небуферизованные каналы синхронизируют goroutine — используйте их как сигнал о завершении работы:

package main
import "fmt"

func compute(result chan<- int) {
    sum := 0
    for i := 1; i <= 100; i++ { sum += i }
    result <- sum
}

func main() {
    ch := make(chan int)
    go compute(ch)
    fmt.Println("Result:", <-ch) // blocks until compute sends
}

Закрытие канала

Отправитель закрывает канал с помощью close(ch), чтобы сообщить, что значения больше отправляться не будут. Получатели обнаруживают закрытие через форму с двумя значениями:

package main
import "fmt"

func generate(ch chan<- int, max int) {
    for i := 0; i < max; i++ {
        ch <- i
    }
    close(ch)  // signal done
}

func main() {
    ch := make(chan int)
    go generate(ch, 5)

    for v := range ch {  // range detects close automatically
        fmt.Println(v)
    }
    fmt.Println("channel closed")
}

Получение двух значений

Получение двух значений v, ok := <-ch сообщает, открыт ли ещё канал:

package main
import "fmt"

func main() {
    ch := make(chan string, 1)
    ch <- "hello"
    close(ch)

    v1, ok1 := <-ch
    fmt.Println(v1, ok1)  // hello true

    v2, ok2 := <-ch
    fmt.Println(v2, ok2)  // "" false — channel closed
}

Отправка в закрытый канал вызывает панику

Отправка в закрытый канал вызывает панику. Только отправитель должен закрывать канал — получатель никогда не должен этого делать:

package main
import "fmt"

func main() {
    ch := make(chan int)
    close(ch)

    // Receiving from closed channel returns zero value
    v, ok := <-ch
    fmt.Println(v, ok)  // 0 false

    // Sending to closed channel panics:
    defer func() { recover() }()
    ch <- 1  // panic: send on closed channel
}

Однонаправленные каналы

Для ясности и безопасности ограничивайте каналы в сигнатурах функций режимом только отправки (chan<- T) или только получения (<-chan T):

package main
import "fmt"

func producer(out chan<- int) {  // send-only
    for i := 0; i < 3; i++ { out <- i }
    close(out)
}

func consumer(in <-chan int) {   // receive-only
    for v := range in { fmt.Println(v) }
}

func main() {
    ch := make(chan int)
    go producer(ch)
    consumer(ch)
}

Пинг-понг между goroutine через каналы

Две goroutine передают сообщение друг другу через каналы — классическая демонстрация синхронизации:

package main
import "fmt"

func ping(ping, pong chan struct{}) {
    for i := 0; i < 3; i++ {
        <-ping
        fmt.Println("ping")
        pong <- struct{}{}
    }
}

func main() {
    ping := make(chan struct{})
    pong := make(chan struct{})
    go func() { for { <-pong; fmt.Println("pong"); ping <- struct{}{} } }()
    go func() { for i := 0; i < 3; i++ { ping <- struct{}{}; <-pong } }()
    // Simplified version:
    fmt.Println("channels enable structured communication")
}

Каналы nil блокируют навсегда

Получение из канала nil или отправка в него блокируется навсегда. Иногда это намеренно используется в операторах select:

package main
import "fmt"

func main() {
    var ch chan int  // nil channel
    fmt.Println(ch == nil) // true

    // select with nil channel — that case is never selected
    active := make(chan int, 1)
    active <- 42
    select {
    case v := <-ch:      // never selected (nil)
        fmt.Println(v)
    case v := <-active:  // selected
        fmt.Println("got:", v)
    }
}

Когда использовать небуферизованные каналы

Используйте небуферизованные каналы, когда:

  • Вам нужна гарантированная синхронная передача
  • goroutine должна подтвердить получение, прежде чем отправитель продолжит работу
  • Вы реализуете семафоры или обратное давление
  • Вы создаёте pipeline, в котором каждый этап должен обрабатывать по одному элементу за раз

Краткая проверка

Что происходит, когда Вы отправляете значение в небуферизованный канал, а готового получателя нет?

Повторение: небуферизованные каналы

Краткое содержание:

  • Создавайте с помощью make(chan T) — нулевая ёмкость
  • Отправка блокируется до готовности получателя; получение блокируется до отправки от отправителя
  • Только отправитель должен закрывать канал
  • range ch перебирает значения до закрытия канала
  • Получение двух значений: v, ok := <-ch
  • Для ясности используйте однонаправленные типы: chan<- и <-chan

Часто задаваемые вопросы

Урок «Небуферизованные каналы» бесплатный?

Да — полный текст урока «Небуферизованные каналы» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Go Academy, подпишись на CoddyKit PRO. Курс Go Academy содержит 4 уроков всего.

Чему я научусь в уроке «Небуферизованные каналы»?

Синхронная передача данных между горутинами Ты практикуешь Go Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Go Academy?

Предыдущий опыт не требуется. Go Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Небуферизованные каналы»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Go Academy?

Да. Каждый урок Go Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Запуск горутин
  2. Небуферизованные каналы
  3. Буферизованные каналы
  4. Направление каналов и шаблоны конвейеров
← Назад к Go Academy