Что такое горутины
Горутина (goroutine) — это легковесный поток выполнения в Go, который позволяет запускать функции конкурентно и асинхронно, не блокируя основной поток. В отличие от потоков операционной системы, горутины имеют малый размер стека (начиная с 2 КБ) и управляются планировщиком Go, что делает их создание и уничтожение очень дешёвыми. Благодаря этому в одной программе могут одновременно работать тысячи и даже миллионы горутин.
Как это работает
Горутины запускаются с помощью ключевого слова go перед вызовом функции. Планировщик Go распределяет горутины по потокам ОС, используя модель M:N, где M горутин отображаются на N потоков. Это позволяет эффективно использовать многоядерные процессоры, не создавая поток под каждую задачу.
Пример запуска горутины:
package main
import (
"fmt"
"time"
)
func printNumbers() {
for i := 1; i <= 5; i++ {
fmt.Println(i)
time.Sleep(100 * time.Millisecond)
}
}
func main() {
go printNumbers() // запуск горутины
// основной поток продолжает выполнение
time.Sleep(1 * time.Second)
}
В этом примере `printNumbers` выполняется конкурентно с `main`. Основная функция не ждёт завершения горутины, поэтому мы добавляем `time.Sleep`, чтобы программа не завершилась раньше.
## Преимущества горутин
- **Эффективность ресурсов**: горутина потребляет всего несколько килобайт памяти, в то время как поток ОС — около 1 МБ. Создание горутины занимает наносекунды, а не микросекунды, как у потоков.
- **Простота синхронизации**: Go предоставляет каналы (`chan`) для безопасного обмена данными между горутинами, что снижает риск гонок данных по сравнению с разделяемой памятью.
- **Высокая производительность**: планировщик Go оптимизирует переключение контекста, что позволяет эффективно использовать параллелизм на многоядерных системах.
## Подводные камни
- **Горутины не останавливаются принудительно**: если горутина блокируется навсегда (например, из-за бесконечного цикла), она может вызвать утечку ресурсов.
- **Гонки данных**: при обращении к общим переменным из нескольких горутин необходимо использовать мьютексы (`sync.Mutex`) или каналы, иначе возможны непредсказуемые результаты.
- **Порядок выполнения не гарантирован**: горутины планируются планировщиком, поэтому нельзя полагаться на порядок их запуска или завершения.
## Когда использовать
Горутины идеально подходят для задач, которые можно выполнять параллельно: обработка сетевых запросов, чтение файлов, фоновые вычисления. Однако для простых последовательных операций они избыточны. Также стоит избегать создания горутин для очень коротких задач, так как накладные расходы на планирование могут превысить выгоду.
## Коротко
- Горутина — лёгкий поток, запускается через `go func()`.
- Память: ~2 КБ на старте, дешёвое создание.
- Синхронизация через каналы или мьютексы.
- Не гарантирован порядок выполнения, возможны гонки данных.