La concurrencia en Go es uno de los aspectos más destacados de este lenguaje de programación. Puede que ya hayas oído hablar de ella y quizá te preguntes qué la hace tan especial. En términos sencillos, la concurrencia implica manejar múltiples tareas al mismo tiempo, mientras que el paralelismo se centra en ejecutar múltiples tareas simultáneamente. Aunque esta diferencia puede parecer sutil, es fundamental para comprender cómo Go optimiza el uso de recursos.
¿Cómo funcionan los hilos de ejecución en Go?
Cuando ejecutamos un programa en Go, este se corre en un hilo de ejecución. Un hilo es una secuencia de instrucciones que el procesador ejecuta. Por ejemplo, si estuviéramos creando un canal en Linux, el programador (imaginemos un gopher o gofer) inicia el proceso y espera a que termine, repitiendo este ciclo si hay varias tareas.
funcmain(){// La función main es la base de nuestras operaciones en Go.}
La función main se ejecuta dentro de una goroutine, la unidad básica de concurrencia en Go. Una vez que el programa ha terminado de ejecutarse, esta goroutine (o gopher) "muere" en términos prácticos, o prefiere "irse a jugar videojuegos", una metáfora que sugiere que es más fácil crear una nueva goroutine que intentar reactivar una que ya ha finalizado.
¿En qué se diferencia el paralelismo de la concurrencia?
En un escenario donde necesitamos crear tres kernels de Linux de forma paralela, el procesador puede dividir las tareas en tres hilos distintos para procesarlas simultáneamente. Cada gopher se queda esperando a que su tarea se complete antes de moverse a la siguiente, asegurando así el trabajo paralelo.
Por otro lado, la concurrencia es más eficiente: el gopher inicia la creación de cada kernel uno por uno, y en lugar de esperar pasivamente a que uno termine, pasa a la siguiente tarea disponible. Esto permite utilizar los recursos de manera más flexible y eficiente.
¿Cuáles son las ventajas de utilizar la concurrencia en Go?
La concurrencia en Go ofrece varios beneficios que potencian el rendimiento y la eficiencia de las aplicaciones:
Optimización de los recursos: Hace posible que múltiples goroutines compartan un hilo, aprovechando al máximo los ciclos del CPU.
Simplicidad: A diferencia de otros lenguajes, Go ofrece primitivas de concurrencia simplificadas que permiten gestionar tareas sin necesidad de lidiar con el complejo manejo manual de hilos.
Escalabilidad: Facilita la creación de aplicaciones más rápidas y escalables, ya que las goroutines facilitan trabajar de manera asíncrona y no bloquean el flujo del programa principal.
La parte más abierta de Go permite manejar tareas concurrentes y paralelas con facilidad. Esta característica, además de su eficiencia, hacen del lenguaje un aliado poderoso para los desarrolladores modernos, especialmente en ambientes de múltiples núcleos y sistemas distribuidos.
La concurrencia no es solo una técnica avanzada, sino una herramienta esencial que multiplica el potencial de Go. Así que sigue adelante, explora y practica su implementación. Aprende a conjugar eficiencia y optimización en tu código y lleva tus habilidades al próximo nivel.
Un ejemplo practico de la vida real puede ser:
Concurrencia: En un banco hay un solo cajero, la persona que esta haciendo su transacción, por algún motivo tiene que esperar por una aprobación, entonces lo hacen esperar a un lado mientras siguen atendiendo a los demás en la fila
Paralelismo: Resulta que en la otra sede de ese banco, hay 3 cajeros, entonces se van atendiendo de manera paralela.
¡Excelente!
Una forma de resumir el paralelismo y la concurrencia es esta:
Paralelismo: 3 filas para pagar y 3 cajas abiertas
Concurrencia: 3 filas para pagar y una caja abierta
Este es un tema que de primera vista es complicado de asimilarlo, por eso les dejo una analogía que me ayudó a entender concurrencia cuando trabajaba con microprocesadores.
Una manera interesante de ver alguno de los problemas con la concurrencia es con el uso de maps.
por ejemplo si ejecutamos el siguiente código:
package main
import"fmt"funcmain(){ m :=make(map[string]int) m["1"]=1 m["2"]=2 m["3"]=3 m["4"]=4 m["5"]=5 m["6"]=6 m["7"]=7 m["8"]=8 m["9"]=9 m["10"]=10for_, value :=range m { fmt.Printf("%d\n", value)}}
Van a haber momentos donde no se va a imprimir en orden correcto.
Si que es extraño ver el resultado
@Tafnes Lorena tiene razón los maps funcionan como HASH no manejan orden.
Imagino que la concurrencia aprovecha mas cada hilo, pues el mismo hilo se encarga de hacer muchas cosas y el paralelismo utiliza mas recursos.
Chequen este video, si no les quedo claro que es la concurrencia, yo entendí el concepto a los 3 minutos del video
Concunrrencia: Permite ejecutar múltiples tareas a la vez sin la necesidad de que estas terminen intercabiando de una a otra.
Paralelismo: De igual manera ejecuta varios procesos a la vez pero en este caso tiene que esperar a que una termine para poder continuar con la otra.
Por lo que entendi de la definicion es que concurrencia puede manejar varias peticiones en un solo input por ejemplo, mientras, paralelismo hace varias cosas en una sola instancia
Les comparto estas slides de Rob Pike uno de los creadores de GO, estan buenisimas!. Pude entender en un instante la diferencia entre concurrencia y paralelismo.
La explicación no es correcta. Los hilos de los nucleos de la CPUs se ejecutan en paralelo por cada ciclo de reloj.