← Назад к вопросам

Всегда ли горутины выполняются параллельно?

2.0 Middle🔥 252 комментариев
#Конкурентность и горутины

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Всегда ли горутины выполняются параллельно?

Нет, горутины не всегда выполняются параллельно. Параллельное выполнение — это лишь потенциальная возможность, а не гарантия. Реальная манера выполнения зависит от нескольких факторов, главный из которых — количество доступных логических процессоров (CPU cores) в системе и модель параллелизма, реализованная в Go.

Ключевое различие: Конкурентность vs Параллелизм

Чтобы понять поведение горутин, нужно разделить два понятия:

  • Конкурентность (Concurrency) — это свойства программы или системы структурировать свою работу как набор независимо выполняемых процессов (горутин). Это модель проектирования. Горутины в Go конкурентны всегда, так как они могут выполняться, приостанавливаться и возобновляться независимо друг от друга, создавая иллюзию одновременной работы.
  • Параллелизм (Parallelism) — это физическое одновременное выполнение двух или более задач на разных процессорных ядрах. Это то, что происходит на уровне железа. Параллелизм возможен только при наличии нескольких ядер и является одним из способов реализации конкурентной программы.

Роль планировщика Go (Goroutine Scheduler)

Планировщик Go (часть runtime) работает в пользовательском пространстве и управляет выполнением горутин на ограниченном количестве потоков операционной системы (OS threads или M). Его основная задача — эффективно распределять многочисленные (легкие) горутины по небольшому количеству (тяжелых) потоков ОС.

package main

import (
    "fmt"
    "runtime"
    "sync"
)

func main() {
    // Устанавливаем использование только ОДНОГО процессора.
    // Это симулирует среду без аппаратного параллелизма.
    runtime.GOMAXPROCS(1)

    var wg sync.WaitGroup
    wg.Add(2)

    go func() {
        defer wg.Done()
        for i := 0; i < 3; i++ {
            fmt.Println("Горутина A:", i)
        }
    }()

    go func() {
        defer wg.Done()
        for i := 0; i < 3; i++ {
            fmt.Println("Горутина B:", i)
        }
    }()

    wg.Wait()
}

При GOMAXPROCS(1) две горутины будут конкурентными, но не параллельными. Планировщик будет переключаться между ними на одном ядре, выполняя их псевдопараллельно (например, вы можете увидеть вывод A:0, A:1, B:0, B:1, A:2, B:2).

Факторы, влияющие на параллельное выполнение

  1. GOMAXPROCS (по умолчанию равно количеству логических ядер CPU). Это верхняя граница количества потоков ОС, которые могут параллельно выполнять пользовательский код Go. Если GOMAXPROCS > 1, планировщик может распределить горутины по разным потокам ОС, и те, в свою очередь, могут быть исполнены на разных ядрах — так возникает истинный параллелизм.

  2. Структура программы. Даже при многих ядрах параллелизм может не достигаться, если:

    *   Горутины преимущественно блокируются на операциях I/O (сеть, файлы).
    *   Программа имеет узкое место (один общий ресурс, `sync.Mutex`), из-за которого большинство горутин стоит в очереди.
    *   Недостаточно горутин, готовых к выполнению (например, все ожидают каналов или таймеров). Планировщик не будет простаивающие ядра загружать работой "просто так".

  1. Работа планировщика. Планировщик принимает решения о переключении горутин в определенных точках (points of preemption), например:
    *   При операциях с каналами.
    *   При вызовах функций из пакета `sync`.
    *   При системных вызовах (например, I/O).
    *   При явном вызове `runtime.Gosched()`.
    *   Каждые 10 мс (примерно) на уровне планировщика.

Практический вывод

  • На однопроцессорной машине или при GOMAXPROCS=1 горутины работают только конкурентно, но не параллельно.
  • На многопроцессорной машине (и при GOMAXPROCS > 1) горутины имеют возможность выполняться параллельно, и планировщик стремится к этому для повышения производительности.
  • Конкурентная модель Go (горутины + каналы) абстрагирует разработчика от низкоуровневых деталей параллелизма. Вы пишете конкурентный код, а среда выполнения (runtime) использует доступный аппаратный параллелизм там, где это возможно и выгодно.

Таким образом, горутины — это инструмент для создания конкурентных программ. Параллелизм же — это оптимизация "под капотом", которая может применяться для ускорения выполнения вашей конкурентной программы на соответствующем оборудовании. Гарантировать параллельное выполнение конкретных горутин в произвольный момент времени нельзя. Можно гарантировать лишь корректность логики их взаимодействия.

Всегда ли горутины выполняются параллельно? | PrepBro