En este módulo, aprenderemos sobre los mutexes y cómo se utilizan para sincronizar el acceso a recursos compartidos en programas concurrentes en Go. La concurrencia es una característica poderosa de Go, pero también puede llevar a problemas como condiciones de carrera si no se maneja correctamente. Los mutexes son una herramienta esencial para evitar estos problemas.

Conceptos Clave

  1. Mutex: Abreviatura de "mutual exclusion" (exclusión mutua), es un mecanismo que permite que solo una goroutine acceda a un recurso compartido a la vez.
  2. Condiciones de Carrera: Ocurren cuando dos o más goroutines acceden a un recurso compartido al mismo tiempo y al menos una de las accesos es una escritura.
  3. Bloqueo y Desbloqueo: Los mutexes tienen dos operaciones principales: Lock y Unlock. Lock bloquea el mutex y Unlock lo desbloquea.

Ejemplo Práctico

Vamos a ver un ejemplo práctico de cómo usar un mutex en Go.

Código sin Mutex (Condición de Carrera)

package main

import (
	"fmt"
	"time"
)

var counter int

func increment() {
	for i := 0; i < 1000; i++ {
		counter++
	}
}

func main() {
	go increment()
	go increment()

	time.Sleep(time.Second)
	fmt.Println("Counter:", counter)
}

En este ejemplo, dos goroutines están incrementando la variable counter al mismo tiempo. Esto puede llevar a una condición de carrera, y el valor final de counter puede no ser el esperado.

Código con Mutex

package main

import (
	"fmt"
	"sync"
	"time"
)

var counter int
var mutex sync.Mutex

func increment() {
	for i := 0; i < 1000; i++ {
		mutex.Lock()
		counter++
		mutex.Unlock()
	}
}

func main() {
	go increment()
	go increment()

	time.Sleep(time.Second)
	fmt.Println("Counter:", counter)
}

En este ejemplo, hemos añadido un mutex para proteger el acceso a la variable counter. Ahora, solo una goroutine puede incrementar counter a la vez, evitando condiciones de carrera.

Explicación del Código

  1. Declaración del Mutex: var mutex sync.Mutex declara un mutex.
  2. Bloqueo y Desbloqueo: mutex.Lock() bloquea el mutex antes de incrementar counter, y mutex.Unlock() lo desbloquea después de la operación.

Ejercicio Práctico

Ejercicio 1: Sincronización de Acceso a un Mapa

Crea un programa que use un mapa para contar la frecuencia de palabras en un texto. Usa goroutines para procesar diferentes partes del texto en paralelo y un mutex para sincronizar el acceso al mapa.

Solución

package main

import (
	"fmt"
	"strings"
	"sync"
)

var wordCount = make(map[string]int)
var mutex sync.Mutex

func countWords(text string, wg *sync.WaitGroup) {
	defer wg.Done()
	words := strings.Fields(text)
	for _, word := range words {
		mutex.Lock()
		wordCount[word]++
		mutex.Unlock()
	}
}

func main() {
	var wg sync.WaitGroup
	texts := []string{
		"hello world",
		"world of Go",
		"hello concurrency",
	}

	for _, text := range texts {
		wg.Add(1)
		go countWords(text, &wg)
	}

	wg.Wait()
	fmt.Println("Word Count:", wordCount)
}

Explicación del Ejercicio

  1. Mapa Compartido: var wordCount = make(map[string]int) declara un mapa para contar las palabras.
  2. Mutex: var mutex sync.Mutex declara un mutex para sincronizar el acceso al mapa.
  3. Goroutines: Se lanzan varias goroutines para procesar diferentes partes del texto en paralelo.
  4. WaitGroup: sync.WaitGroup se usa para esperar a que todas las goroutines terminen.

Resumen

En esta sección, hemos aprendido sobre los mutexes y cómo se utilizan para sincronizar el acceso a recursos compartidos en programas concurrentes en Go. Hemos visto ejemplos prácticos y ejercicios para reforzar los conceptos. Los mutexes son una herramienta esencial para evitar condiciones de carrera y asegurar que los programas concurrentes funcionen correctamente.

En el próximo módulo, exploraremos más sobre la concurrencia en Go, incluyendo el uso de canales y la instrucción select.

© Copyright 2024. Todos los derechos reservados