Fundamentos de Concurrencia
10 ejemplos para empezar con Concurrencia - 7 básicos y 3 intermedios.
Busca en todas las páginas de la documentación
10 ejemplos para empezar con Concurrencia - 7 básicos y 3 intermedios.
mkdir conc && cd conc && go mod init example.com/conc.main.go y ejecútalo con go run ..go test -race ./... cuando los ejemplos usen estado compartido.Inicia trabajo concurrentemente con go.
package main
import (
"fmt"
"time"
)
func main() {
go func() {
fmt.Println("desde la goroutine")
}()
time.Sleep(100 * time.Millisecond) // espera para que la salida aparezca antes de salir
fmt.Println("desde main")
}go programa la función en una nueva goroutine; retorna inmediatamente.main puede salir antes de que la goroutine imprima.sync.WaitGroup o canales sobre time.Sleep en código real.Relacionado: Concurrencia en Go: Goroutines y Canales Primero - Modelo mental CSP
El emisor y el receptor se encuentran en el canal.
package main
import "fmt"
func main() {
ch := make(chan string)
go func() { ch <- "ping" }()
msg := <-ch
fmt.Println(msg)
}make(chan string) crea un canal sin búfer - capacidad cero.Relacionado: Canales: Reglas de Búfer, Sin Búfer y Cierre - reglas de búfer y cierre
Desacopla el emisor y el receptor hasta la capacidad.
package main
import "fmt"
func main() {
ch := make(chan int, 2)
ch <- 1
ch <- 2
fmt.Println(<-ch, <-ch)
}make(chan int, 2) permite dos envíos sin un receptor esperando.Relacionado: Canales: Reglas de Búfer, Sin Búfer y Cierre - cuándo usar búfer
Espera a que un lote de goroutines termine.
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
fmt.Println("worker", n)
}(i)
}
wg.Wait()
fmt.Println("done")
}Add antes de go, Done en un defer dentro del worker.Wait se bloquea hasta que el contador vuelve a cero.Relacionado: sync.Mutex, RWMutex y WaitGroup - detalles de WaitGroup
Señala la finalización cerrando desde el emisor.
package main
import "fmt"
func main() {
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
close(ch)
for v := range ch {
fmt.Println(v)
}
}range vacía hasta que el canal está cerrado y vacío.close; enviar en un canal cerrado provoca un pánico.ok == false en la forma de dos valores.Relacionado: Canales: Reglas de Búfer, Sin Búfer y Cierre - reglas de propiedad
select espera operaciones de canal (aquí, una sola recepción).
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan string)
go func() {
time.Sleep(50 * time.Millisecond)
ch <- "ready"
}()
select {
case msg := <-ch:
fmt.Println(msg)
}
}select se bloquea hasta que un caso puede proceder.default para comportamiento sin bloqueo.Relacionado: Sentencia select y Casos Default - patrones de multiplexación
Cuando múltiples goroutines mutan datos, bloquéalos.
package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.Mutex
count := 0
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
mu.Lock()
count++
mu.Unlock()
}()
}
wg.Wait()
fmt.Println(count)
}Mutex serializa secciones críticas - aquí count++.Lock con Unlock; defer mu.Unlock() después de Lock es idiomático.-race para detectar bloqueos faltantes.Relacionado: sync.Mutex, RWMutex y WaitGroup - disciplina de bloqueo
Evita esperar para siempre por trabajo lento.
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan string)
select {
case msg := <-ch:
fmt.Println(msg)
case <-time.After(100 * time.Millisecond):
fmt.Println("timeout")
}
}time.After devuelve un canal que se activa después de la duración.context.Context en lugar de sleep ad hoc.Relacionado: Sentencia select y Casos Default - recetas de tiempo de espera
Un productor, una goroutine consumidora.
package main
import "fmt"
func main() {
jobs := make(chan int, 5)
done := make(chan struct{})
go func() {
for j := range jobs {
fmt.Println("procesado", j)
}
close(done)
}()
for i := 1; i <= 3; i++ {
jobs <- i
}
close(jobs)
<-done
}jobs permite que el worker range salga limpiamente.done señala que el consumidor terminó - alternativa a WaitGroup.Relacionado: Goroutines: Costo de Creación y Visión General de la Programación - programación de muchos workers
Compila con -race para detectar accesos no sincronizados.
package main
import (
"sync"
"testing"
)
func TestCounter(t *testing.T) {
var wg sync.WaitGroup
n := 0
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
n++ // carrera: descomenta mu del ejemplo 7 para arreglar
}()
}
wg.Wait()
}go test -race - el detector de carreras informa accesos conflictivos.-race en CI para paquetes que usan goroutines.Relacionado: Detector de Carreras: Ejecución e Interpretación de Salida - lectura de la salida de carreras
Versiones de Stack: Esta página fue escrita para Go 1.26.x (predeterminado Green Tea GC, modernizadores go fix - verifica el parche en la compilación), chi (última - verifica en la compilación), gin (última - verifica en la compilación), echo (última - verifica en la compilación), google.golang.org/grpc (última - verifica en la compilación), sigs.k8s.io/controller-runtime (última - verifica en la compilación), kubebuilder (última - verifica en la compilación), tinygo (última - verifica los objetivos de placa en la compilación), wazero (última - verifica en la compilación) y golangci-lint (última - verifica el conjunto de linters en la compilación).
Revisado por Chris St. John·Última actualización: 19 jul 2026