ProgramaciónDesarrollador Backend

Explica qué es una gorutina en Go. ¿Cómo funciona internamente y en qué se diferencia de los hilos en otros lenguajes?

Supere entrevistas con el asistente de IA Hintsage

Respuesta

Una gorutina es un hilo ligero de ejecución, gestionado por el runtime de Go. Para iniciar una nueva gorutina, se utiliza la palabra clave go antes de la llamada a la función. Internamente, se crea una estructura que describe el stack y el estado de la tarea, que se añade a la cola del planificador de gorutinas.

A diferencia de los hilos del sistema operativo, una gorutina tiene un stack inicial mucho menor (normalmente 2 KB), y el stack se amplía automáticamente según sea necesario. El planificador de Go asigna automáticamente gorutinas a los hilos disponibles del sistema operativo (modelo M:N).

Las principales diferencias con los hilos (threads):

  • Se crean más rápido y requieren menos memoria
  • Son programados por el runtime de Go, no por el sistema operativo
  • Se escalan automáticamente según los núcleos
  • La sincronización se realiza a través de canales, lo que previene muchas clases de condiciones de carrera

Ejemplo de uso de gorutinas y canales:

package main import ( "fmt" "time" ) func worker(id int, jobs <-chan int, results chan<- int) { for j := range jobs { fmt.Printf("worker %d comenzó trabajo %d\n", id, j) time.Sleep(time.Second) fmt.Printf("worker %d terminó trabajo %d\n", id, j) results <- j * 2 } } func main() { jobs := make(chan int, 5) results := make(chan int, 5) for w := 1; w <= 3; w++ { go worker(w, jobs, results) } for j := 1; j <= 5; j++ { jobs <- j } close(jobs) for a := 1; a <= 5; a++ { <-results } }

Pregunta con trampa

¿Pueden las gorutinas de Go ejecutarse en paralelo en múltiples núcleos?

A menudo se recibe la respuesta incorrecta: "No, porque Go utiliza hilos verdes". En realidad, mediante una variable de entorno o la llamada runtime.GOMAXPROCS(n), Go puede paralelizar la ejecución de gorutinas en todos los núcleos disponibles del procesador.

Ejemplo:

import "runtime" func main() { runtime.GOMAXPROCS(4) // Permite usar 4 núcleos ... }

Ejemplos de errores reales debido al desconocimiento de los matices del tema


Historia

En un proyecto de servicio backend en Go, se implementó un pool de trabajadores a través de gorutinas, pero los programadores olvidaron limitar la cantidad de gorutinas que podían ejecutarse simultáneamente. Como resultado, al aumentar la carga, la aplicación lanzaba miles de gorutinas, lo que llevó al agotamiento de la memoria y al colapso del servicio. El problema se resolvió imponiendo un límite a las gorutinas activas (por ejemplo, mediante un semáforo o un pool de trabajadores).


Historia

Uno de los empleados sincronizó incorrectamente los datos entre gorutinas, utilizando variables globales normales sin mutexes o canales. Esto provocó una condición de carrera (race condition), lo que causó que se produjeran errores periódicamente durante el procesamiento de pagos. El problema solo se detectó después de ejecutarse en producción.


Historia

En el servicio de parsing se pasó por alto el momento de pasar canales nil en select: después de cerrar el canal, select continuó esperando datos de manera bloqueante, y parte de las gorutinas "se colgaron". Se corrigió asignando nil al canal cerrado y manejando correctamente el select.