Você escreve `go func()` e acha que criou uma thread. Não criou. O runtime do Go multiplexa milhares de goroutines em um punhado de threads do SO. Spawna 1000 aqui embaixo e olha o número de threads quase não se mexer.
Bloqueadas em syscall (cada uma segura uma M dedicada)
0Goroutines (G)
4P (GOMAXPROCS)
4Threads do SO (M)
0Trocas de contexto
rodando (na M)na fila do Pbloqueada em syscall
G = goroutine, P = processador lógico (quantos rodam em paralelo), M = thread do SO. O scheduler do Go encaixa muitos G em poucos M, com uma fila por P e work stealing: um P ocioso rouba metade da fila do P mais cheio. Por isso 1000 goroutines não viram 1000 threads.
Goroutine
Thread do SO
Stack inicial
~2 KB (cresce)
~1 MB (fixa)
Criação
barata
cara (syscall)
Agendamento
user space
kernel
Quantidade
centenas de milhares
milhares
Em Go
funcmain() {
runtime.GOMAXPROCS(4) // nº de P (paralelismo)var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
gofunc(id int) { // 1000 goroutines, ~2KB cadadefer wg.Done()
work(id)
}(i)
}
wg.Wait() // espera todas terminarem
}
🧠 Desafio — Goroutines & Scheduler
Mexe no visualizador (spawna goroutines, muda o GOMAXPROCS, bloqueia em syscall) antes de responder. As duas últimas são de reflexão: escreve a sua e só então revela o modelo.