Буферизованный канал
Буферизованный канал - это канал с буфером, который позволяет отправлять данные без блокировки, пока буфер не заполнится, и принимать данные без блокировки, пока буфер не опустеет. При создании канала указывается его вместимость. Когда горутина отправляет значение в буферизованный канал, операция не блокируется, если в буфере есть свободное место. Аналогично, получение данных не блокируется, если буфер не пуст.
Синтаксис
make(chan Type, capacity)
Первый параметр определяет тип данных, которые будут передаваться через канал, второй - размер буфера. Для отправки данных используется оператор <-, а для приема - оператор <-.
Пример
Создадим буферизованный канал с вместимостью 2 и отправим в него два значения без блокировки:
package main
import "fmt"
func main() {
ch := make(chan string, 2)
ch <- "first"
ch <- "second"
fmt.Println(<-ch)
fmt.Println(<-ch)
}
Результат выполнения кода:
"first"
"second"
Отправка двух значений не блокируется, так как буфер вмещает оба. После этого мы получаем их в порядке очереди.
Пример
Если буфер переполнен, отправка блокируется до тех пор, пока не появится свободное место. Аналогично, если буфер пуст, получение блокируется до появления данных. Рассмотрим пример с горутинами:
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int, 2)
go func() {
time.Sleep(2 * time.Second)
ch <- 42
fmt.Println("Sent 42")
}()
fmt.Println("Waiting for value...")
value := <-ch
fmt.Println("Received:", value)
}
Результат выполнения кода:
"Waiting for value..."
"Sent 42"
"Received: 42"
Горутина отправляет значение после задержки. Основная горутина блокируется при попытке получения данных, так как буфер пуст, и ожидает, пока значение не будет отправлено.
Пример
Использование буферизованного канала для ограничения количества одновременно выполняемых горутин. Это полезно для управления параллелизмом:
package main
import (
"fmt"
"sync"
)
func main() {
const maxWorkers = 2
sem := make(chan struct{}, maxWorkers)
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
sem <- struct{}{}
defer func() { <-sem }()
fmt.Printf("Worker %d started\n", id)
fmt.Printf("Worker %d finished\n", id)
}(i)
}
wg.Wait()
}
Результат выполнения кода:
"Worker 3 started"
"Worker 3 finished"
"Worker 5 started"
"Worker 5 finished"
"Worker 4 started"
"Worker 4 finished"
"Worker 1 started"
"Worker 1 finished"
"Worker 2 started"
"Worker 2 finished"
В этом примере канал sem используется как семафор. Он позволяет запускать не более двух горутин одновременно. Каждая горутина отправляет пустую структуру в канал перед началом работы и получает ее после завершения, освобождая место для следующей.
Смотрите также
-
канал
chan,
который описывает базовый синтаксис и принцип работы каналов -
небуферизованный канал
unbuffered-chan,
который не имеет буфера и требует одновременной готовности отправителя и получателя -
горутину
goroutine,
которая используется для параллельного выполнения кода с каналами -
оператор выбора
select,
который позволяет ожидать операции с несколькими каналами