La concurrencia en Golang es clave para las aplicaciones modernas

DANIEL ÁVALOS / SOFTWARE ENGINEER
Golang (Go) es un lenguaje de programación diseñado desde su origen para facilitar y optimizar la programación concurrente, la cual es fundamental para afrontar el desafío de desarrollar aplicaciones capaces de manejar un gran volumen de solicitudes.

Ante esta necesidad, se ha convertido en una herramienta esencial para crear software robusto, eficiente y confiable que permita escalar verticalmente, agregando más recursos, como CPU y memoria, conforme sea necesario.

¿Qué es la concurrencia?

En programación, la concurrencia permite que diferentes partes de un programa se ejecuten de manera independiente, lo que puede mejorar la eficiencia y el rendimiento, especialmente en sistemas con múltiples núcleos o procesadores.

Existen varias formas de implementar la concurrencia, como hilos, procesos, o coroutines, y cada una tiene sus ventajas, dependiendo del caso de uso.

Un ejemplo de concurrencia en la vida real es un restaurante, donde mientras un chef está cocinando, los meseros están atendiendo a los clientes y tomando pedidos. La cocina y el servicio de mesas operan simultáneamente, permitiendo que todas las tareas se realicen al mismo tiempo sin necesidad de esperar a que una tarea finalice antes de comenzar otra.

¿Por qué Golang es ideal para la concurrencia?

Go es un lenguaje de programación que fue diseñado para abordar los retos de la concurrencia desde su creación. Con un enfoque minimalista, pero poderoso, nos brinda herramientas integradas que permiten a los desarrolladores manejar la concurrencia de manera natural y eficiente, evitando los errores comunes que surgen en otros lenguajes.

Elementos esenciales para manejar la concurrencia en Golang:

  • Goroutine: una goroutine es una función que se ejecuta concurrentemente con otras funciones en Go, permitiendo que múltiples tareas se realicen simultáneamente dentro del mismo programa.

Canales para comunicación: un canal en Go es una estructura que permite a las goroutines enviar y recibir datos de manera segura y sincronizada.

  • Sincronizar con sync.WaitGroup: un wait group es una herramienta que permite esperar a que un grupo de goroutines termine su ejecución antes de continuar.
  • Proteger secciones críticas con sync.Mutex: un sync.Mutex en Go es un mecanismo de sincronización que asegura que sólo una goroutine acceda a una sección crítica del código a la vez, evitando conflictos y condiciones de carrera.
  • Ciclo de vida con contexto: el ciclo de vida de un context en Go gestiona la duración de una operación, permitiendo la cancelación y el control de tiempos de espera para goroutines y operaciones concurrentes.

Worker pools: gestión eficiente de tareas concurrentes

Un worker pool es una estructura de programación que gestiona un grupo de "trabajadores" o "workers" (goroutines en Go) que realizan tareas concurrentemente. La idea es tener un conjunto limitado de trabajadores que toman tareas de una cola de trabajo y las procesan en paralelo. Esto ofrece varios beneficios clave:

  • Control de recursos: limita el número de goroutines activas para evitar el uso excesivo de memoria y CPU.
  • Manejo eficiente de tareas: permite procesar tareas en paralelo sin sobrecargar el sistema.
  • Mejora de rendimiento: reduce el costo de crear y destruir goroutines repetidamente.
  • Escalabilidad: facilita la gestión de recursos al controlar el número de goroutines.

A continuación, se muestra un ejemplo de un programa que procesa 20 órdenes aleatorias, sumando el total de cada orden con el siguiente. En un programa sin concurrencia, el procesamiento de las 20 órdenes tarda 12.55 segundos. En contraste, el programa que utiliza concurrencia con cinco trabajadores (goroutines) completa el procesamiento en sólo 3.6 segundos.

La concurrencia es fundamental para optimizar el rendimiento y la eficiencia en el desarrollo de aplicaciones modernas, debido a que permite ejecutar múltiples tareas simultáneamente, aprovechando al máximo los recursos del sistema y mejorando la capacidad de respuesta y la escalabilidad de las aplicaciones.

Implementar concurrencia de manera efectiva, como se hace en Go, con goroutines y canales, puede transformar significativamente la velocidad y la eficiencia del procesamiento de datos.