Escribiendo tu primer programa concurrente en Go en Ubuntu

El lenguaje de programación Go de Google ha estado presente desde 2009 y en 2012, el lenguaje alcanzó su estado oficial v1.0. En esos años intermedios, mucho ha cambiado, incluyendo cómo se instala el lenguaje. En sus inicios, no había distribuciones binarias oficiales y tenías que compilar Go desde el código fuente, que era el método recomendado ya que el lenguaje estaba cambiando con frecuencia, o usar un paquete precompilado para tu distribución de Linux. En ese entonces, el soporte para Windows era limitado, al igual que el soporte para arquitecturas de CPU distintas a Intel.

Las cosas han mejorado enormemente desde entonces. Para Linux, hay dos formas fáciles de instalar Go. Descarga la versión binaria oficial de Linux desde la página de descargas de Go o opta por un paquete precompilado para tu distribución de Linux. La forma más fácil de instalar Go en Ubuntu es usar apt-get:

sudo apt-get install golang

Una vez que Go esté instalado, puedes comenzar a desarrollar programas. Uno de los programas Go más simples es el clásico programa “¡Hola Mundo!”. Usando un editor de texto, crea un archivo llamado “ hellomte.go “ con el siguiente corto código Go:

package main  
  
import "fmt"  
  
func main(){  
     fmt.Println("¡Hola Make Tech Easier!")  
}

Desde la v1.0 de Go, se ha eliminado la necesidad de comandos individuales de compilación y enlace y los antiguos comandos 8g y 8l han sido reemplazados por el comando go.

Para ejecutar hellomte.go, abre una terminal y cambia el directorio a la carpeta que contiene el archivo de código fuente, luego escribe:

go run hellomte.go

Esto compilará y ejecutará el programa Go, pero no producirá un binario ejecutable. Para crear un binario y luego ejecutarlo, usa el comando go build:

go build hellomte.go  
./hellomte

El poder de la concurrencia

Una de las características definitorias del lenguaje de programación Go es su soporte para la concurrencia, que permite que un programa trabaje con múltiples tareas a la vez. El paralelismo, que es similar a la concurrencia, permite que un programa ejecute muchas tareas simultáneamente, pero la concurrencia va un paso más allá en que permite que estas tareas separadas se comuniquen e interactúen. Como resultado, Go permite a los programadores utilizar una variedad de diseños concurrentes diferentes, incluyendo grupos de trabajadores, tuberías (donde una tarea ocurre después de otra) y tareas de fondo sincrónicas o asincrónicas. La base de esta concurrencia es la goroutine junto con canales y la declaración select de Go.

Aquí hay un simple programa Go que imprime una cadena varias veces usando una goroutine concurrente:

package main  
  
import(  
"fmt"  
"time"  
)  
  
func say(s string){  
for i := 0; i < 5; i++ {  
        fmt.Println(s)  
}  
}  
  
func main(){  
go say("¡Hola Make Tech Easier!")  
    fmt.Println("Duerme un poco...")  
    time.Sleep(100 * time.Millisecond)  
}

La función say() simplemente realiza un bucle simple para imprimir la cadena (parámetro s) cinco veces. Lo interesante es cómo se llama a esa función. En lugar de simplemente llamar a say("¡Hola Make Tech Easier!"), la palabra clave go se coloca delante de la llamada a la función. Esto significa que la función se ejecutará como una tarea separada. El resto de la función main() simplemente duerme un poco para dar tiempo a que la goroutine complete.

La salida puede sorprenderte:

go-lang-concurrent

Como puedes ver, la función say() se ejecuta como una goroutine y mientras se está configurando, el resto de la función main() continúa, imprimiendo “Duerme un poco…” y luego yéndose a dormir. Para entonces, la goroutine está activa y comienza a imprimir la cadena cinco veces. Finalmente, el programa termina una vez que se ha alcanzado el límite de tiempo.

Canales

Las goroutines pueden comunicarse usando canales. Un canal abre una línea de comunicación entre dos partes diferentes de un programa Go. Típicamente, una función se llamará como una goroutine y si necesita enviar datos de vuelta (digamos de una operación de red), puede usar un canal para pasar esos datos. Si otra parte del programa Go está esperando esos datos, dormirá hasta que los datos estén listos. Los canales se crean usando la función make() y pueden ser pasados como parámetros a las goroutines.

Considera este código:

package main  
  
import(  
"fmt"  
)  
  
func say(s string, c chan int){  
var i int  
for i = 0; i < 5; i++ {  
          fmt.Println(s)  
}  
     c <- i  
}  
  
func main(){  
     c := make(chan int)  
go say("¡Hola Make Tech Easier!", c)  
     sts := <- c  
     fmt.Println(sts)  
}

La función say() es muy similar al primer ejemplo con la excepción de que el segundo parámetro es un canal y que después de imprimir la cadena, el número de iteraciones se enviará por el canal a través de la línea de código c <- i.

La función principal crea un canal, inicia la función say() como una goroutine y luego espera a que los datos lleguen por el canal, sts := <- c antes de imprimir el resultado.

Conclusión

El lenguaje Go ha progresado significativamente en los últimos años, si no lo has revisado recientemente, ¡quizás ahora sea un buen momento!