Sobes.tech
Middle+

Cuéntenos sobre el patrón de gestión de concurrencia (GMP) en el lenguaje Go.

sobes.tech AI

Respuesta de la IA

GMP es un modelo de planificación de la ejecución de goroutines en Go, donde:

  • G (Goroutine): Hilo de ejecución ligero, una abstracción sobre los hilos del sistema.
  • M (Machine): Hilo del sistema operativo. Puede ejecutar el código de una o varias goroutines.
  • P (Processor): Procesador lógico, que representa un contexto para la ejecución de goroutines. A cada P se le asigna un M, y P contiene una cola local de goroutines en estado runnable. La cantidad de P por defecto es $GOMAXPROCS (generalmente el número de núcleos del procesador).

Funciona así:

  1. El planificador (parte del runtime de Go) coloca nuevas goroutines en las colas global o locales de P.
  2. Un M, asociado a un P, toma una goroutine de la cola de P y la ejecuta.
  3. Cuando una goroutine se bloquea (por ejemplo, esperando I/O o en un mutex):
    • El M se desacopla del P actual.
    • El planificador intenta encontrar otro M para que ocupe ese P, o crea uno nuevo.
    • La goroutine bloqueada se coloca en una cola especial.
    • Cuando se libera el bloqueo, la goroutine vuelve a estar en estado runnable y regresa a la cola de P.
  4. Cuando la goroutine agota su quantum de tiempo o cede explícitamente el control (rara vez), el planificador puede cambiar el M a otra goroutine en el mismo P.

Ventajas de este enfoque:

  • Uso eficiente de los hilos del sistema M.
  • Reducción de los costos de cambio de contexto en comparación con los hilos nativos del sistema operativo.
  • Balanceo de carga entre P mediante el mecanismo de "work stealing" (M puede robar una goroutine de la cola de otro P).

Ejemplo de creación de una goroutine:

// main.go
package main

import (
	"fmt"
	"time"
)

func worker(id int) {
	fmt.Printf("Trabajador %d iniciando\n", id)
	time.Sleep(time.Second) // Ilustración de bloqueo/ocupación
	fmt.Printf("Trabajador %d terminado\n", id)
}

func main() {
	for i := 1; i <= 5; i++ {
		go worker(i) // Crear una nueva goroutine
	}

	time.Sleep(time.Second * 2) // Esperar a que terminen las goroutines
}