справочник
Горутины и каналы
«Не общайтесь через разделяемую память — разделяйте память через общение». Основной инструмент — горутина и канал, а не мьютекс.
Горутины
Горутина — лёгкий поток, управляемый рантаймом Go. Запуск — ключевое слово
go. Их могут быть сотни тысяч: стек начинается с пары килобайт и растёт по нужде.
func main() {
go say("привет") // в отдельной горутине
say("мир") // в текущей
}
func say(s string) {
for i := 0; i < 3; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
Каналы
Канал передаёт значения между горутинами и синхронизирует их. Отправка и приём блокируются, пока другая сторона не готова — если канал небуферизованный.
ch := make(chan int) // небуферизованный
go func() { ch <- 42 }() // отправка
v := <-ch // приём: 42
buf := make(chan int, 8) // буфер на 8 значений
Закрытый канал больше не принимает отправку, но приём из него отдаёт оставшиеся
значения, а затем нулевые. Идиома перебора — for range:
close(buf)
for v := range buf {
fmt.Println(v)
}
select
select ждёт сразу на нескольких каналах — готовый выигрывает; если готовы
несколько, выбор случайный. Ветка default делает операцию неблокирующей.
select {
case v := <-in:
process(v)
case out <- result:
// отправили
case <-time.After(time.Second):
return errTimeout // таймаут
}
Когда каналов мало: sync
Для простой защиты разделяемого состояния — sync.Mutex; дождаться группу
горутин — sync.WaitGroup; выполнить единожды — sync.Once.
var wg sync.WaitGroup
for _, u := range urls {
wg.Add(1)
go func(u string) {
defer wg.Done()
fetch(u)
}(u)
}
wg.Wait() // ждём завершения всех
Отмену и дедлайны across горутин удобно нести через context.Context —
он же связывает цепочку вызовов в HTTP-сервере.