← Go EspañolChapter 11 of 13

Concurrencia

## Objetivos de Aprendizaje - Comprender goroutines - Trabajar con canales - Dominar operaciones de canal - Usar sentencias select - Manejar condiciones de carrera - Implementar patrones comunes ## Goroutines ### ¿Qué es una Goroutine? Una goroutine es un hilo ligero gestionado por el runtime de Go. ### Sintaxis Básica ```go go f(x, y, z) // Inicia una nueva goroutine ``` ### Ejemplo ```go func main() { go sayHello() fmt.Println("Hola desde main") time.Sleep(time.Second) // Esperar goroutine } func sayHello() { fmt.Println("Hola desde goroutine") } ``` ### Función Anónima ```go go func() { fmt.Println("Ejecutando en goroutine") }() time.Sleep(time.Second) ``` ## Canales ### Declaración ```go ch := make(chan int) // Sin buffer ch := make(chan int, 10) // Con buffer de capacidad 10 ``` ### Enviar y Recibir ```go ch := make(chan int) // Enviar ch <- 42 // Recibir value := <-ch ``` ### Operaciones de Canal ```go ch := make(chan string, 2) ch <- "Hola" // Enviar ch <- "Mundo" // Enviar msg1 := <-ch // Recibir msg2 := <-ch // Recibir ``` ### Cerrar ```go ch := make(chan int) go func() { ch <- 1 ch <- 2 close(ch) }() for v := range ch { fmt.Println(v) } ``` ## Canales Direccionales ### Especificación ```go chan T // Puede enviar y recibir chan<- T // Solo envío <-chan T // Solo recepción ``` ### Casos de Uso ```go // Productor: solo puede enviar func producer(ch chan<- int) { ch <- 42 } // Consumidor: solo puede recibir func consumer(ch <-chan int) { value := <-ch fmt.Println(value) } ``` ## Sentencia Select ### select Básico ```go select { case msg1 := <-ch1: fmt.Println("Recibido de ch1:", msg1) case msg2 := <-ch2: fmt.Println("Recibido de ch2:", msg2) case sendVal := <-ch3: fmt.Println("Listo para enviar:", sendVal) default: fmt.Println("Sin comunicación") } ``` ### Esperar en Múltiples Canales ```go select { case msg := <-ch1: fmt.Println("ch1:", msg) case msg := <-ch2: fmt.Println("ch2:", msg) case <-time.After(time.Second): fmt.Println("Tiempo agotado") } ``` ### Comunicación No Bloqueante ```go select { case msg := <-ch: fmt.Println("Recibido:", msg) default: fmt.Println("Sin mensaje listo") } ``` ## Patrones de Canales ### Pipeline ```go func generate(nums ...int) <-chan int { out := make(chan int) go func() { for _, n := range nums { out <- n } close(out) }() return out } func square(in <-chan int) <-chan int { out := make(chan int) go func() { for n := range in { out <- n * n } close(out) }() return out } func main() { for n := range square(square(generate(1, 2, 3, 4, 5))) { fmt.Println(n) } } ``` ### Fan-out, Fan-in ```go func merge(channels ...<-chan int) <-chan int { out := make(chan int) var wg sync.WaitGroup for _, ch := range channels { wg.Add(1) go func(c <-chan int) { for v := range c { out <- v } wg.Done() }(ch) } go func() { wg.Wait() close(out) }() return out } ``` ### Worker Pool ```go func worker(id int, jobs <-chan int, results chan<- int) { for j := range jobs { fmt.Printf("worker %d procesando trabajo %d\n", id, j) results <- j * 2 } } func main() { jobs := make(chan int, 100) results := make(chan int, 100) 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 } } ``` ## Condiciones de Carrera ### Problema ```go var counter int func increment() { counter++ // ¡No es atómico! } func main() { for i := 0; i < 1000; i++ { go increment() } time.Sleep(time.Second) fmt.Println(counter) // Probablemente no es 1000 } ``` ### Solución con Mutex ```go import "sync" var ( counter int mu sync.Mutex ) func increment() { mu.Lock() defer mu.Unlock() counter++ } func main() { var wg sync.WaitGroup for i := 0; i < 1000; i++ { wg.Add(1) go func() { increment() wg.Done() }() } wg.Wait() fmt.Println(counter) // 1000 } ``` ### WaitGroup ```go var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(1) go func() { defer wg.Done() doWork(i) }() } wg.Wait() // Bloquear hasta que todos terminen ``` ## Paquete sync ### Mutex ```go var mu sync.Mutex func criticalSection() { mu.Lock() defer mu.Unlock() // Código protegido } ``` ### RWMutex ```go var ( mu sync.RWMutex data map[string]int ) func read(key string) int { mu.RLock() defer mu.RUnlock() return data[key] } func write(key string, value int) { mu.Lock() defer mu.Unlock() data[key] = value } ``` ### Once ```go var ( once sync.Once single *Config ) func getConfig() *Config { once.Do(func() { single = &Config{} }) return single } ``` ### Map (Concurrente) ```go var syncMap sync.Map syncMap.Store("key", "value") value, ok := syncMap.Load("key") syncMap.Delete("key") syncMap.Range(func(k, v interface{}) bool { fmt.Printf("%s: %s\n", k, v) return true }) ``` ### Pool ```go pool := sync.Pool{ New: func() interface{} { return make([]byte, 1024) }, } buf := pool.Get().([]byte) defer pool.Put(buf) ``` ## Paquete Context ### WithCancel ```go import "context" ctx, cancel := context.WithCancel(context.Background()) go func() { time.Sleep(2 * time.Second) cancel() }() select { case <-ctx.Done(): fmt.Println("¡Cancelado!") case <-time.After(3 * time.Second): fmt.Println("Tiempo agotado") } ``` ### WithTimeout ```go ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() select { case <-ctx.Done(): fmt.Println("Tiempo agotado") default: fmt.Println("Procediendo...") } ``` ### WithValue ```go ctx := context.WithValue(context.Background(), "user", "alice") if user, ok := ctx.Value("user").(string); ok { fmt.Println("Usuario:", user) } ``` ## Errores Comunes ### Enviar a Canal Cerrado ```go ch := make(chan int) close(ch) ch <- 1 // PANIC: envío a canal cerrado ``` ### Canal Nil ```go var ch chan int // canal nil <-ch // Bloquea por siempre ch <- 1 // Bloquea por siempre ``` ### Deadlock ```go ch := make(chan int) ch <- 1 // Bloquea (sin receptor) <-ch // Nunca llega aquí ``` ## Resumen - Goroutines: hilos ligeros vía palabra clave `go` - Canales: tuberías tipadas para comunicación - Canales con buffer: `make(chan T, capacity)` - Canales sin buffer: `make(chan T)` (envíos bloquean hasta recibir) - `select`: esperar en múltiples canales - `sync.Mutex`, `sync.RWMutex` para exclusión mutua - `sync.WaitGroup` para esperar goroutines - `sync.Map` para acceso concurrente a map - `context` para cancelación y tiempos límite - Nunca cerrar desde el receptor; cerrar solo desde el emisor

Comments

Comments powered by Giscus

To enable comments, add your Giscus embed code here.

Learn more about Giscus →