No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Implementación de Goroutines y channels

26/42
Recursos

Aportes 24

Preguntas 4

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

o inicia sesión.

Si se incluye “fmt.Println(<- chanel)” en el mismo ciclo for del “go” no se obtiene el mismo rendimiento.

for _, server := range servers {
	go checkServer(server, chanel)
	fmt.Println(<- chanel)
}

Quería aclarar algunas cosas

  1. La primera vez que ejecuta el programa, no aparecen los mensajes, porque primero se acaba de ejecutar el main, que las subrutinas, esto se podría solucionar con un sleep que es muy mala practica, un waitgroup que espera que se ejecuten todas las gorutinas https://gobyexample.com/waitgroups o con canales

  2. Como esta tarea no requiere compartir las respuestas entre las go rutinas waitgroup es una muy buena opción

  3. Quisiera aclarar que la go rutina no va a esperar hasta que acabe la primera para continuar. Trataré de explicarlo

Si tenemos dos gorutinas , que tiene que ejecutar una función

 --  Go rutina 1 
 --  Go rutina 2

func Sumar(num1, num2 int){
	imprime un mensaje
	hace la suma 
	la envia por el canal 
	c <- resultado
}

el proceso sería asi:

Ambas gorutinas empiezan, imprimen, suman, pero al momento de enviar por el canal la primera envia porque nadie ha usado el canal, pero la segunda se queda en pausa, esperando que la primera envie la información, cuando ya el mensaje haya sido recibido por alguien, ahi si la segunda envie su mensaje
  1. Se puede hacer el for de la siguiente manera
 for elem := range queue {
        fmt.Println(elem)
    } 

La palabra clave go lo que hace es crear un nuevo proceso o hilo que se ejecuta independiente de main.

Main al mandar a ejecutar goroutines, aunque no hayan acabado, termina la ejecución de su código. Channels es el mecanismo de comunicación entre las mismas goroutines y también el proceso main.

// definición de un channel.
c := make(chan string)

// sintaxis para transmitir información al channel
c <- "sending data"

// sintaxis para recibir información del channel
received := <-c

Los channels alojan un tipo específico de dato que se debe definir explícitamente.

Si únicamente pusiéramos una vez la recepción de información de un channel, main continuaría con su ejecución, porque no especificamos que tenía que esperar más información. Para recibir la información de múltiples goroutines, tenemos que definir las veces que nuestro channel debe esperar información.

Por otro lado, si no recibiera información se quedaría bloqueado, porque utilizamos un channel de tipo unbuffered, un tipo de channel que no definimos su capacidad o tamaño de manera explícita, que se quedaría esperando a recibir un información.

Me pueden ayudar para saber porque me sale el error to many arguments in call to checkStatusServer
have (string, chan string)
want (string)

El codigo con el ejercicio funcionó de maravilla pero me quedó la duda de porque a mí si me salió está advertencia y al tutor no.

package main

import (
	"fmt"
	"net/http"
	"time"
)

func main() {
	startWatch := time.Now()
	channel1 := make(chan string)
	servers := []string{
		"http://platzi.com",
		"http://google.com",
		"http://facebook.com",
		"http://instagram.com",
	}

	for _, server := range servers {
		go checkStatusServer(server, channel1)

	}

	for index, _ := range servers {
		fmt.Println(<-channel1, index+1)
	}

	timeExec := time.Since(startWatch)

	fmt.Printf("Execution time: %s", timeExec)
}

func checkStatusServer(server string, channel chan string) {
	_, err := http.Get(server)
	if err != nil {
		//fmt.Println(server, "Is not Available =(")
		channel <- server + " Is not Available =("
	} else {
		//fmt.Println(server, "It's working OK")
		channel <- server + " It's working OK"
	}
}

No se si estoy mal, pero sin las rutinas el proceso tomo 2.086 segundos, y usando rutinas tomo 2.736 segundos, entonces no veo la mejora al usar las Goroutines o no se implementarion bien, porque aunque los servidores no siempre responden en un tiempo fijo la idea idea era mejorar los tiempos y 0.7seg es bastante para los llamados

Les comparto el mismo codigo pero con waiting groups 😉

package main

import (
	"fmt"
	"net/http"
	"sync"
	"time"
)

func main() {
	start := time.Now()

	servers := []string{
		"https://www.festivalestereopicnic.com/",
		"https://www.eltiempo.com/",
		"https://platzi.com/",
	}
	var wg sync.WaitGroup
	for _, server := range servers {
		wg.Add(1)
		go checkServer(server, &wg)
	}
	wg.Wait()
	finalTime := time.Since(start)
	fmt.Println("Execution time", finalTime)
}

func checkServer(server string, wg *sync.WaitGroup) {
	_, err := http.Get(server)
	if err != nil {
		fmt.Println(server, "Not available")
	} else {
		fmt.Println(server, "Is UP :) ")
	}
	wg.Done()
}

Se nota la diferencia 😃

Les dejo mi código, chequea cada link cada 5 segundos.

package main

import (
	"fmt"
	"net/http"
	"time"
)

func main() {
	URLS := []string{
		"platzi",
		"google",
		"facebook",
		"instagram",
	}
	c := make(chan string, len(URLS))

	for _, u := range URLS {
		c <- u
	}

	for u := range c {
		go reviewServer(u, c)
	}
}

func reviewServer(u string, c chan<- string) {
	_, err := http.Get(u)
	if err != nil {
		fmt.Println(u, "is down.")
	} else {
		fmt.Println(u, "is up.")
	}

	time.Sleep(5 * time.Second)
	c <- u
}

¿Es posible iterar sobre el canal con un for range?

package main

import (
	"fmt"
	"net/http"
	"time"
)

func main() {
	inicio := time.Now()

	ch := make(chan string)
	servidores := []string{
		"h t t p   : / / p  l a tz i . c o m",
		"h t t p : // g o o g le . c o m ",
		"h t t p : / / f ac e b o o k . c o m ",
		"h t t p : / / I n s t a g ra m .   co  m",
	}

	for _, servidor := range servidores {
		go revisarServidor(servidor, ch)
	}

	for i := 0; i < len(servidores); i++ {
		fmt.Println(<-ch)
	}

	tiempoPasado := time.Since(inicio)
	fmt.Printf("Done in %s", tiempoPasado)
}

func revisarServidor(servidor string, ch chan string) {
	_, err := http.Get(servidor)

	if err != nil {
		ch <- servidor + "no esta disponible"
	} else {
		ch <- servidor + "esta funcionando"
	}

}

package main

import (
	"fmt"
	"net/http"
	"time"
)

func main() {
	channel := make(chan string, 4)
	links := []string{
		"https://www.digitalocean.com",
		"https://jsonplaceholder.typicode.com",
		"https://platzi.com/",
		"https://github.com/",
	}
	start := time.Now()
	go func() {
		for _, url := range links {
			if 	_, err := http.Get(url); err != nil {
				fmt.Println("El servidor esta fuera!")
			} else {
				channel <- fmt.Sprintf("%s --> Ok", url)
			}
		}
		defer close(channel)
	}()

	for msg := range channel {
		fmt.Println(msg)
	}
	end := time.Since(start)
	fmt.Println(end)
}

la combinación Goroutines y channels hacen de Go un lenguaje muy poderoso.

Que poder tan abismal tiene Go routines y que sencillo fue implementarlo.
según entiendo Go administra los hilos para no tener que abrir tantos hilos como en otros lenguajes.
Esta es una ventaja total de go contra el resto.

Los channel nos dice que paso con la ejecucion de las gorutines

con la palabra reservada **chan string ** no dice que estamos declarando un canal con su tipo de datos

canal es el channel y con la flecha <- lo que esta a la derecha de la flecha sera el contenido de lo que se esta transmitiendo

Con gorutines utilizamos programacion concurrente, para que se secuencial.

De esta manera imprimira solo el nombre del servidor por ejemplo, “platzi”, y no toda la URL “http://platzi.com

<code>servers := []string{
		"platzi",
		"youtube",
		"google",
		"instagram",
		"facebook",
		"twitter",
	}

	for _, server := range servers {
		go checkStatus("http://"+server+".com", channel)
	}

	for i := 0; i < len(servers); i++ {
		avaible := <-channel

		fmt.Println(avaible)
	}

	finishProcessTime := time.Since(startProcessTime)

	fmt.Printf("estimated time to get result: ")
	fmt.Println(finishProcessTime)

}

func checkStatus(server string, channel chan string) {
	_, err := http.Get(server)
	svWithExtension := strings.Split(server, "//")
	svName := strings.Split(svWithExtension[1], ".")
	sv := svName[0]
	if err != nil {
		channel <- sv + " status: NO AVAIBLE"
	}
	channel <- sv + " status: AVAIBLE"

Obtienes el mismo resultado al escribir el Println dentro del for con la concurrencia:

//recorrer slice
	for _, servidor := range servidores{
		go revisarServidor(servidor, canal)
		//leer un canal
		fmt.Println(<-canal)
	}```

con esto reduces las lineas de codigo 

Me pasó que las rutinas morian antes de terminar de ejecutarse .-. pensé que habia hecho algo mal xd

package main

import (
	"fmt"
	"net/http"
	"time"
)

func main() {
	canal := make(chan string)
	servidores := []string{
		"https://platzi.com" , 
		"https://google.com",
		"https://facebokkkkok.com" , 
		"https://Instagram.com",
	}
	
	fmt.Print("Iniciando revisión de servidores sin aplicar concurrencia...\n\n")
	inicio := time.Now()
	for _, servidor := range servidores {
		revisarServidorSinConcurrencia(servidor)
	}

	tiempoPasado := time.Since(inicio)
	fmt.Printf("\n--------------> Done in %s <--------------\n\n", tiempoPasado)
	fmt.Print("Iniciando revisión de servidores aplicando concurrencia...\n\n")

	inicio2 := time.Now()
	for _, servidor := range servidores {
		go revisarServidorConConcurrencia(servidor, canal)
	}

	for i := 0; i < len(servidores); i++ {
		fmt.Println(<-canal)
	}

	tiempoPasado2 := time.Since(inicio2)
	fmt.Printf("\n--------------> Done in %s <--------------\n\n", tiempoPasado2)

}

func revisarServidorConConcurrencia(servidor string, canal chan string) {
	_, err := http.Get(servidor)

	if err != nil {
		canal <- servidor + " no esta disponible"
	} else {
		canal <- servidor + " funciona correctamente"
	}

}

func revisarServidorSinConcurrencia(servidor string) {
	_, err := http.Get(servidor)

	if err != nil {
		fmt.Println(servidor, "no esta disponible")
	} else {
		fmt.Println(servidor, "funciona correctamente")
	}

}

quería resaltar que el asincronismo y la concurrencia no son lo mismo.

soy el unico que nota que los canales aumentan levemente el tiempo de ejecucion ?

Para crear un canal con varios valores como un objeto puedes crear un struct

//MessageStruct Struct for message
type MessageStruct struct {
	message    string
	status    bool
	err error
}

c := make(chan MessageStruct)

Esto creara un canal como un objeto con un message, status y err.