Каналы

Канал — типизированная «труба», через которую горутины передают друг другу значения. Оператор <- используется и для отправки, и для получения.

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

Отправка блокируется, пока другая горутина не примет значение:

ch := make(chan int)

go func() {
    ch <- 42 // отправка
}()

v := <-ch // получение
fmt.Println(v)

Буферизованный канал

Отправка блокируется только когда буфер заполнен:

ch := make(chan string, 2)
ch <- "a"
ch <- "b"
fmt.Println(<-ch, <-ch)

Закрытие и range

func gen(n int) <-chan int {
    out := make(chan int)
    go func() {
        defer close(out)
        for i := 0; i < n; i++ {
            out <- i
        }
    }()
    return out
}

for v := range gen(5) {
    fmt.Println(v)
}

Закрывает канал только отправитель. Чтение из закрытого канала возвращает нулевое значение; проверить состояние можно так: v, ok := <-ch.

select

select ждёт готовности одного из нескольких каналов:

select {
case msg := <-ch1:
    fmt.Println("из ch1:", msg)
case msg := <-ch2:
    fmt.Println("из ch2:", msg)
case <-time.After(time.Second):
    fmt.Println("таймаут")
}

Пул воркеров

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        results <- j * j
    }
}

func main() {
    jobs := make(chan int, 100)
    results := make(chan int, 100)

    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }
    for j := 1; j <= 5; j++ {
        jobs <- j
    }
    close(jobs)

    for i := 0; i < 5; i++ {
        fmt.Println(<-results)
    }
}

Направленные каналы

Это помогает компилятору ловить ошибки и делает API понятнее.