El lenguaje de programación GO es particularmente amigable para desarrollar programas con concurrencia, es decir, poder “lanzar”
tasks simultáneamente en forma asincrónica para que la computadora se haga cargo de ellos y así reducir tiempos.
Un método sencillo es fire and forget, es decir, lanzar varios tasks y al final esperar a la terminación de los mismos antes de salir del programa.
El método más útil resulta ser el lanzar simultáneamente varios procesos idénticos que resuelven porciones del problema e ir agrupando los resultados, de esta forma, para un problema largo o complejo, se tienen varios workers que hacen más veloz la resolución.
Así, se definen 10 workers, se los lanza con la sentencia go, los cuales tienen como parámetro un identificador, el waitgroup que sirve para que el worker avise cuando ha terminado decrementando el contador de worker activos, un canal de ingreso de datos y uno de resultado.
Al worker le entra un trabajo en el canal jobChannel el cual está esperando mediante el loop, ejecuta la función doSomething y devuelve un resultado al canal de resultChannel.
package main
import (
"fmt"
"sync"
"time"
)
type Job struct {
Id int
}
type JobResult struct {
Output string
}
func worker(id int, wg *sync.WaitGroup, jobChannel <-chan Job, resultChannel chan JobResult) {
defer wg.Done()
for job := range jobChannel {
resultChannel <- doSomething(id, job)
}
}
func main() {
start := time.Now() // Inicializa una variable donde guardar el tiempo
var jobs []Job
for i := 0; i < 100; i++ {
jobs = append(jobs, Job{Id: i})
}
const NumberOfWorkers = 10
var wg sync.WaitGroup
wg.Add(NumberOfWorkers) jobChannel := make(chan Job)
jobResultChannel := make(chan JobResult, len(jobs))
// Lanza los workers
for i := 0; i < NumberOfWorkers; i++ {
go worker(i, &wg, jobChannel, jobResultChannel)
}
// Envía trabajo a los worker
for _, job := range jobs {
jobChannel <- job
}
// Una vez enviado los trabajos, se cierra el canal close(jobChannel)
wg.Wait()
close(jobResultChannel)
var jobResults []JobResult
// Llegan resultados de los workers
// El loop, al igual que en los workers, levanta un valor
// siempre que haya uno y mientras el canal esté abierto.
for result := range jobResultChannel {
jobResults = append(jobResults, result)
}
fmt.Println(jobResults)
fmt.Printf("Took %s", time.Since(start))
}
func doSomething(workerId int, job Job) JobResult {
fmt.Printf("Worker #%d Running job #%d\n", workerId, job.Id)
time.Sleep(500 * time.Millisecond)
return JobResult{Output: "Success"}
}