Concurrencia en Go: Goroutines y Canales Primero
Go trata la concurrencia como una característica de primera clase del lenguaje.
Busca en todas las páginas de la documentación
Go trata la concurrencia como una característica de primera clase del lenguaje.
Inicias trabajo con go, coordinas con canales y recurres a primitivas de sync solo cuando el estado compartido es el modelo más simple.
Esta página es el ancla conceptual de la sección de Concurrencia.
Conceptos básicos de concurrencia recopila fragmentos ejecutables; los artículos hermanos cubren la planificación, las reglas de cierre de canales, select, mutexes y el detector de carreras.
Imagina un programa Go como un conjunto de goroutines —tareas ligeras planificadas por el runtime— que pasan datos a través de canales.
Un canal es un conducto tipado: chan int transporta valores int entre goroutines.
Enviar (ch <- v) y recibir (v := <-ch) son los puntos de sincronización.
Un canal sin búfer bloquea al emisor hasta que un receptor está listo, y bloquea al receptor hasta que llega un emisor.
Ese enlace es un evento de sincronización: el envío ocurre antes de que la recepción se complete.
Un canal con búfer desacopla al emisor y al receptor hasta su capacidad; los envíos se bloquean solo cuando el búfer está lleno, las recepciones solo cuando está vacío.
El famoso proverbio de Go —No comuniques compartiendo memoria; comparte memoria comunicando— es un consejo práctico, no una prohibición de los mutexes.
Significa: prefiere pasar la propiedad de los datos a través de canales para que solo una goroutine mute un valor a la vez.
Cuando varias goroutines deben leer y actualizar el mismo mapa o contador, sync.Mutex o sync/atomic suele ser la herramienta adecuada.
Iniciar una goroutine es go f() o go func() { ... }().
La goroutine principal es el punto de entrada del programa; cuando main retorna, el proceso termina y las goroutines pendientes se terminan sin esperar.
Usa sync.WaitGroup, canales o context para coordinar el apagado.
El planificador del runtime utiliza el modelo GPM: G (goroutine), P (procesador lógico que contiene una cola de ejecución), M (hilo del SO).
Los P están vinculados a los M; cuando una goroutine se bloquea en E/S o en un canal, el planificador la aparca y ejecuta otra G en el mismo M.
Por eso puedes generar miles de goroutines donde los hilos del SO se ahogarían.
select multiplexa operaciones de canal: se bloquea hasta que un caso puede proceder, o ejecuta default para un intento sin bloqueo.
Los tiempos de espera son idiomáticos: select entre el trabajo y <-time.After(d).
Cerrar un canal señala "no más valores"; los receptores se enteran a través de la forma de dos valores v, ok := <-ch donde ok es falso después de cerrar.
Solo el emisor debe cerrar; enviar en un canal cerrado provoca un pánico.
goroutine principal
│
├── go worker ──► ch ──► go consumer
│ │
│ └── close(ch) cuando termine
│
└── wg.Wait() / vaciar resultados
| Enfoque | Fortaleza | Debilidad | Mejor Ajuste |
|---|---|---|---|
| Canal sin búfer | Sincronización fuerte, traspaso claro | Acopla el tiempo del productor/receptor | Despacho de trabajos, pipelines de acuse de recibo |
| Canal con búfer | Suaviza ráfagas | Oculta la contrapresión si el tamaño es incorrecto | Fan-in de logs, colas limitadas |
sync.Mutex | Actualizaciones simples de estructuras compartidas | Fácil de interbloquear si se anida incorrectamente | Cachés, índices en memoria |
sync/atomic | Contadores/banderas rápidos | Limitado a operaciones numéricas/de puntero | Métricas, recuentos de referencias |
select + default | Sondeos sin bloqueo | Bucles ocupados si se usan incorrectamente | Tiempos de espera, intentos de envío/recepción |
Los servidores HTTP en chi, gin y echo manejan cada solicitud en su propia goroutine; la concurrencia es la forma predeterminada del código de red de Go.
Los streams y pools de trabajadores de google.golang.org/grpc superponen canales y cancelación de contexto bajo los manejadores de RPC.
Los reconciliadores de controller-runtime se ejecutan concurrentemente; las cachés de informadores compartidas usan mutexes internamente mientras los eventos fluyen a través de canales.
Los servicios de producción combinan patrones: los pools de trabajadores limitados controlan el paralelismo, context.Context propaga los plazos y errgroup cancela a los hermanos ante el primer error (cubierto en Concurrencia Avanzada).
Ejecuta pruebas y compilaciones de CI con -race para detectar escrituras de mapas no sincronizadas y errores de "comprobar y actuar".
golangci-lint incluye comprobaciones de patrones propensos a carreras; empareja linters con pruebas de integración que ejercitan rutas concurrentes.
Realiza perfiles antes de generar goroutines ilimitadas por elemento; la memoria para las pilas y la sobrecarga del planificador se acumulan bajo un fan-out extremo.
ok o range.append no sincronizadas son comunes.Communicating Sequential Processes es un modelo donde procesos independientes intercambian mensajes.
Las goroutines y canales de Go implementan esa idea sin una sintaxis de proceso separada: la concurrencia vive en el lenguaje.
Prefiere canales al pasar propiedad o elementos de trabajo entre etapas.
Prefiere mutexes cuando varias goroutines actualizan los mismos campos de estructura con frecuencia.
Que main retorne finaliza el proceso.
Espera con sync.WaitGroup, vacía un canal de resultados o bloquéate en select hasta que los trabajadores señalen que han terminado.
El emisor se bloquea para siempre, un interbloqueo si ninguna otra goroutine puede recibir.
Siempre empareja emisores con receptores o usa búfer/tiempos de espera.
Solo si una goroutine escribe o si sincronizas el acceso.
Pasa una copia o envía la slice a través de un canal; las carreras de append no sincronizadas son comunes.
Si varios casos están listos, Go elige uno pseudoaleatoriamente.
No confíes en el orden de prioridad sin reestructurar el bucle.
No: GOMAXPROCS establece los P lógicos (ejecución paralela de hilos del SO).
Puedes ejecutar muchas más goroutines que GOMAXPROCS.
No: las goroutines ayudan cuando el trabajo puede superponerse (E/S, etapas paralelas).
El trabajo serial intensivo en CPU con entradas pequeñas a menudo se ejecuta más rápido secuencialmente.
El modelo de memoria define los bordes de ordenación: el envío de canal ocurre antes de la recepción, mutex.Unlock ocurre antes de un Lock posterior, etc.
Las carreras violan esos bordes.
No: el detector de carreras añade sobrecarga y es para pruebas y staging.
Envía compilaciones sin race; ejecuta -race en CI en paquetes que usan concurrencia.
select y Casos default - multiplexación y tiempos de esperaVersiones de Stack: Esta página fue escrita para Go 1.26.x (Green Tea GC por defecto, go fix modernizers - verificar parche en la compilación), chi (última - verificar en la compilación), gin (última - verificar en la compilación), echo (última - verificar en la compilación), google.golang.org/grpc (última - verificar en la compilación), sigs.k8s.io/controller-runtime (última - verificar en la compilación), kubebuilder (última - verificar en la compilación), tinygo (última - verificar objetivos de placa en la compilación), wazero (última - verificar en la compilación) y golangci-lint (última - verificar conjunto de linters en la compilación).
Revisado por Chris St. John·Última actualización: 16 jul 2026