Ответ
В Go управление параллелизмом осуществляется не через прямое управление потоками ОС, а через горутины — легковесные потоки, управляемые рантаймом Go.
GOMAXPROCS
Переменная runtime.GOMAXPROCS
определяет, сколько потоков ОС могут одновременно исполнять код Go. По умолчанию это значение равно количеству доступных ядер ЦП. Это не ограничивает число горутин, а лишь задает степень реального параллелизма.
// Устанавливает количество потоков ОС равным количеству ядер
runtime.GOMAXPROCS(runtime.NumCPU())
Способы ограничения количества горутин
Для ограничения количества одновременно работающих горутин (конкарренси) используются следующие паттерны:
-
Пул воркеров (Worker Pool): Создается фиксированное количество горутин-воркеров, которые разбирают задачи из общего канала.
tasks := make(chan int, 100) var wg sync.WaitGroup // Запускаем 5 воркеров for i := 0; i < 5; i++ { wg.Add(1) go func() { defer wg.Done() for task := range tasks { // Обработка задачи } }() } // ... добавляем задачи в канал tasks ... close(tasks) wg.Wait() // Ждем завершения всех воркеров
-
Канал-семафор: Канал с буфером используется для ограничения входа в критическую секцию.
var maxConcurrency = 5 sem := make(chan struct{}, maxConcurrency) for _, task := range tasks { sem <- struct{}{} // Занимаем слот go func(t Task) { defer func() { <-sem }() // Освобождаем слот process(t) }(task) }
-
Семафор из пакета
sync
: Более современный и идиоматичный способ, доступный вgolang.org/x/sync/semaphore
.import "golang.org/x/sync/semaphore" var ( maxWorkers = int64(5) sem = semaphore.NewWeighted(maxWorkers) ctx = context.Background() ) for _, task := range tasks { if err := sem.Acquire(ctx, 1); err != nil { // Обработка ошибки break } go func(t Task) { defer sem.Release(1) process(t) }(task) }
Важно: Несмотря на легковесность горутин, их неограниченное создание для ресурсоемких операций (например, сетевые запросы, работа с БД) может привести к исчерпанию файловых дескрипторов, памяти или других системных ресурсов.