Sobes.tech
Middle

O que é o tempo de execução (runtime) e como funciona o escalonador (scheduler)?

sobes.tech IA

Resposta da IA

O tempo de execução (runtime) em Go é o ambiente onde os programas Go são executados. Inclui:

  • Gestão de memória: O coletor de lixo gerencia automaticamente a alocação e liberação de memória.
  • Agendador (scheduler): Gerencia a execução de goroutines em threads do sistema operativo.
  • Chamadas ao sistema: Fornece uma interface para interagir com o sistema operativo.
  • Primitivas de sincronização: Canais, mutexes e outros mecanismos para coordenar goroutines.

O agendador do Go implementa o modelo M:N (muitas goroutines em muitos threads do sistema operativo). Seus componentes principais:

  • G (Goroutine): Um thread leve de execução, basicamente uma abstração sobre uma função.
  • M (Machine): Um thread do sistema operativo onde os programas Go são executados.
  • P (Processor): Um processador lógico associado a um thread do sistema operativo. Ele mantém uma fila local runnable de goroutines.

Princípio de funcionamento do agendador:

  1. As goroutines (G) são criadas e adicionadas a uma fila global ou local runnable.
  2. Os threads do sistema operativo (M) são associados a processadores lógicos (P). A quantidade de P por padrão é igual ao número de núcleos CPU (GOMAXPROCS).
  3. P pega uma goroutine da sua fila local. Se a fila local estiver vazia, P tenta "roubar" (steal) goroutines da fila de outro P ou da fila global.
  4. M executa a goroutine.
  5. Quando uma goroutine fica bloqueada (por exemplo, ao ler de um canal sem dados ou ao fazer uma chamada ao sistema), M se desassocia de P, e P fica disponível para executar outra goroutine. A goroutine bloqueada é colocada em espera. Quando o bloqueio é removido, a goroutine volta a estar runnable e é colocada de volta na fila.
  6. Se M fica bloqueado numa chamada ao sistema, ele não mantém P. Um novo M pode ser criado ou um existente pode ser usado para que P possa continuar a executar outras goroutines.

Esta abordagem permite usar eficientemente os recursos do processador e escalar facilmente tarefas paralelas, escondendo a complexidade da gestão de threads do sistema operativo do desenvolvedor.

// Exemplo de criação de uma goroutine
func myFunc() {
	// Código da goroutine
}

func main() {
	go myFunc() // Início da goroutine
	// O programa principal continua a executar em paralelo
}