Contadores atómicos en Go concurrente

Beginner

This tutorial is from open-source community. Access the source code

Introducción

Esta práctica se centra en la gestión del estado en Go utilizando el paquete sync/atomic para contadores atómicos accedidos por múltiples goroutines.

Contadores atómicos

El problema consiste en incrementar un contador exactamente 1000 veces utilizando 50 goroutines y el paquete sync/atomic.

  • Utilice el paquete sync/atomic para incrementar el contador.
  • Utilice un WaitGroup para esperar a que todas las goroutines terminen su trabajo.
## Esperamos obtener exactamente 50.000 operaciones. Si hubiéramos
## utilizado el no atómico `ops++` para incrementar el contador,
## probablemente obtendríamos un número diferente, que variaría
## entre ejecuciones, porque las goroutines se interpondrían
## entre sí. Además, obtendríamos errores de carrera de datos
## al ejecutar con la bandera `-race`.
$ go run atomic-counters.go
ops: 50000

## A continuación veremos los mutexes, otra herramienta para
## la gestión del estado.

A continuación se muestra el código completo:

// El principal mecanismo para la gestión del estado en Go es
// la comunicación a través de canales. Vimos esto por ejemplo
// con [piscinas de trabajadores](worker-pools). Sin embargo,
// hay otras opciones para la gestión del estado. Aquí
// veremos cómo utilizar el paquete `sync/atomic` para
// _contadores atómicos_ accedidos por múltiples goroutines.

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

func main() {

	// Utilizaremos un entero sin signo para representar nuestro
	// contador (que siempre es positivo).
	var ops uint64

	// Un WaitGroup nos ayudará a esperar a que todas las goroutines
	// terminen su trabajo.
	var wg sync.WaitGroup

	// Iniciaremos 50 goroutines que cada una incrementará el
	// contador exactamente 1000 veces.
	for i := 0; i < 50; i++ {
		wg.Add(1)

		go func() {
			for c := 0; c < 1000; c++ {
				// Para incrementar el contador de forma atómica
				// utilizamos `AddUint64`, pasándole la dirección
				// de memoria de nuestro contador `ops` con la
				// sintaxis `&`.
				atomic.AddUint64(&ops, 1)
			}
			wg.Done()
		}()
	}

	// Espera hasta que todas las goroutines hayan terminado.
	wg.Wait()

	// Ahora es seguro acceder a `ops` porque sabemos
	// que ninguna otra goroutine está escribiendo en él.
	// También es posible leer atómicos de forma segura
	// mientras se están actualizando, utilizando funciones
	// como `atomic.LoadUint64`.
	fmt.Println("ops:", ops)
}

Resumen

En esta práctica, aprendimos cómo utilizar el paquete sync/atomic para gestionar el estado en Go al incrementar un contador utilizando múltiples goroutines. La función AddUint64 se utilizó para incrementar el contador de forma atómica, y un WaitGroup se utilizó para esperar a que todas las goroutines terminaran su trabajo.