Может ли в Go количество горутин превышать количество потоков ОС или ядер процессора?

Ответ

Да, может, и это одна из ключевых особенностей Go, обеспечивающая высокую производительность для конкурентных задач.

Горутины (goroutines) — это легковесные потоки, управляемые средой выполнения Go (Go runtime), а не напрямую операционной системой. Они работают поверх небольшого пула системных потоков ОС.

Go использует модель планирования M:N, где M горутин выполняются на N потоках ОС (где N обычно равно количеству доступных ядер CPU).

Ключевые преимущества горутин перед потоками ОС:

  • Низкое потребление памяти: Стек горутины начинается всего с ~2 КБ, в то время как стек системного потока занимает ~1 МБ.
  • Быстрое переключение: Переключение между горутинами происходит в пространстве пользователя (user-space) и управляется планировщиком Go, что намного быстрее, чем переключение системных потоков, требующее вызова ядра ОС.
  • Масштабируемость: Благодаря этим особенностям, приложение на Go может легко поддерживать сотни тысяч и даже миллионы одновременно работающих горутин.

Пример:

package main

import (
    "fmt"
    "runtime"
    "time"
)

func main() {
    fmt.Printf("Количество ядер CPU: %dn", runtime.NumCPU())

    // Запускаем 10 000 горутин
    for i := 0; i < 10000; i++ {
        go func(n int) {
            // Каждая горутина выполняет небольшую работу
            time.Sleep(time.Millisecond * 10)
        }(i)
    }

    fmt.Printf("Текущее количество горутин: %dn", runtime.NumGoroutine())

    // Даем время на выполнение части горутин
    time.Sleep(time.Second)
    fmt.Printf("Количество горутин после ожидания: %dn", runtime.NumGoroutine())
}