Crea una cuenta o inicia sesión

¡Continúa aprendiendo sin ningún costo! Únete y comienza a potenciar tu carrera

Repaso general: GoRoutines y apuntadores

4/30
Recursos

¿Qué son y cómo usar correctamente las Go Routines y canales en Go?

En el mundo de la programación concurrente, las Go Routines y los canales de comunicación son elementos esenciales que pueden mejorar significativamente el rendimiento de tus proyectos en Go. Entender su funcionamiento te permitirá crear aplicaciones más eficientes y robustas. A continuación, te explicamos sus fundamentos, cómo implementarlos en tu código, y terminamos con los conceptos de apuntadores para ofrecerte un panorama más amplio de su uso práctico.

¿Cómo funcionan las Go Routines y su implementación?

Las Go Routines son la herramienta fundamental en Go para llevar a cabo tareas de manera concurrente. Piensa en ellas como funciones que corren de manera asíncrona en el mismo hilo o en hilos diferentes, permitiéndote realizar múltiples tareas a la vez sin bloquear el flujo del programa principal.

Para lanzar una Go Routine, simplemente utilizas la palabra reservada go antes de llamar a la función deseada:

func DoSomething() {
    time.Sleep(3 * time.Second)
    fmt.Println("done")
}

func main() {
    go DoSomething()
    fmt.Println("This will print before 'done' due to asynchrony")
}

Como nota, una Go Routine por sí sola no es monitoreada, lo que significa que el mensaje "done" podría no imprimirse si main termina antes de que DoSomething se complete. Aquí es donde entran los canales.

¿Qué rol juegan los canales en la comunicación entre Go Routines?

Los canales en Go permiten que múltiples Go Routines se comuniquen entre sí. Un canal es esencialmente una tubería a través de la cual puedes enviar y recibir valores entre diferentes partes de tu programa.

Para interactuar mediante un canal, primero debes crearlo y luego modificar tu función para que acepte el canal como parámetro:

func DoSomething(c chan int) {
    time.Sleep(3 * time.Second)
    fmt.Println("done")
    c <- 1 // Envia un valor al canal
}

func main() {
    c := make(chan int)
    go DoSomething(c)
    <-c // Espera a recibir un valor del canal antes de continuar
}

En este ejemplo, main se bloqueará hasta que DoSomething envíe un valor al canal, asegurando que la función 'done' se imprima antes de que el programa finalice por completo.

¿Cuál es la importancia de los apuntadores en Go?

Los apuntadores en Go son perfectamente necesarios cuando necesitas manejar un valor por su referencia, en lugar de por su copia. Esto es útil para optimizar el uso de memoria y lograr que las funciones modifiquen el estado de una variable fuera de su ámbito local.

Imagina que tienes la variable g y quieres manejar su dirección de memoria:

var g = 25
fmt.Println(g) // Imprime 25

var h *int = &g // h es un apuntador a la dirección de g
fmt.Println(h)  // Imprime la dirección de memoria de g

Ahora, para acceder al valor real donde apunta h, utilizamos el operador asterisco (*):

fmt.Println(*h) // Imprime 25, el valor almacenado en la dirección apuntada por h

Es esencial distinguir entre el uso del operador dirección (&) para referirse a la dirección de memoria de una variable, y el operador asterisco para acceder al valor almacenado en esa dirección. Este conocimiento te salva de posibles errores e inconvenientes en el manejo de memoria en Go.

Finalmente, si estás interesado en adquirir un conocimiento más profundo sobre Go, te recomendamos seguir los cursos preliminares disponibles. Continuar explorando programación orientada a objetos es el siguiente paso recomendable para fortalecer tus habilidades de desarrollo en Go. Sigue aprendiendo y mejorando tus proyectos. ¡Nos vemos en la próxima clase!

Aportes 13

Preguntas 0

Ordenar por:

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

Codigo acumulado de las clases:

package main

import (
	"fmt"
	"strconv"
	"time"
)

func main() {
	var x int
	x = 8
	y := 7
	fmt.Println(x)
	fmt.Println(y)

	// Capturando valor y error
	myValue, err := strconv.ParseInt("NaN", 0, 64)

	// Validando errores.
	if err != nil {
		fmt.Println("%v\n", err)
	} else {
		fmt.Println(myValue)
	}

	// Mapa clave valor.
	m := make(map[string]int)
	m["key"] = 6
	fmt.Println(m["key"])

	// Slice de enteros.
	s := []int{1, 2, 3}
	for index, value := range s {
		fmt.Println(index)
		fmt.Println(value)
	}
	s = append(s, 16)
	for index, value := range s {
		fmt.Println(index)
		fmt.Println(value)
	}

	//c := make(chan int)
	//go doSomething(c)
	//<-c

	g := 25
	fmt.Println(g) // imprime el valor entero 25
	h := &g
	fmt.Println(h) // imprimer la direccion de memoria.
	i := *h
	fmt.Println(i) // Imprime el valor por de g
}

func doSomething(c chan int) {
	time.Sleep(3 * time.Second)
	fmt.Println("done")
	c <- 1
}

Gorutines and chanels

package main

import "time"

func main() {
	c := make(chan int)
	go doSomething(c)
	go doOtherThing()
	go doSomething(c)
	<- c
	print("Lo imprimirá?")

}

func doSomething(c chan int)  {
	time.Sleep(3 * time.Second)
	println("Do something")
	c <- 1
}

func doOtherThing(){
	println("quien se imprimirá primero?")
}

Codigo de esta clase comentado y explicado:

package main

import (
	"fmt"
	"time"
)

func main() {
	c := make(chan int) //Creamos un canal para monitorear las Goroutines
	go doSomething(c)   //Llamamos a la función con go para generar un Goroutine
	<-c                 //main esperara a que este canal reciba el mensaje

	g := 25
	fmt.Println(g)
	h := &g         //Apuntador a g
	fmt.Println(h)  //Dirección de memoria donde esta almacenada g
	fmt.Println(*h) //Accedemos al valor de h que a su vez almacena g
}

func doSomething(c chan int) { //Recibimos un canal de tipo int
	time.Sleep(3 * time.Second) //Poner a dormir
	fmt.Println("Done")
	c <- 1 // le envia el valor de 1
}

En la parte de Apuntador tambien hay que tener en cuenta que si es cambiado el valor de “g” , la variable “h” tambien tendra el nuevo valor de g

g := 25

fmt.Println(g)

h := &g
fmt.Println(h)
fmt.Println(*h)
g = g + 50
fmt.Println("g:", g, " h: ", *h) \\ g: 75 h: 75

Aca dejo otro ejemplo simple de lo que es una goroutine Go con ejemplos

Le doy 5🌟 a JS por tener una manera tan sencilla de trabajar el asincronismo. Hasta ahora en GO solo veo su sintaxis muy rústica y poco amigable con el dev. Puede ser su performance un punto muy fuerte a la hora de destacar, pero solo por eso..
en "Lecturas recomendadas" el vinculo de "... Servidor web..." esta roto.. 404
**Aun no entiendo muy bien, que son exactamente los gorutines, por que se imprime "done" y no 1?**
package main

import (
	"fmt"
	"strconv"
	"time"
)

func main() {
	var x int
	x = 5
	fmt.Println(x)

	myValue, error := strconv.ParseInt("y", 0, 8)

	if error != nil {
		fmt.Printf("%v\n", error)
	} else {
		fmt.Println(myValue)
	}

	mapa := make(map[string]int)

	mapa["llave"] = 15

	fmt.Println(mapa["llave"])

	mySlice := []int{6, 5, 4}

	for index, value := range mySlice {
		fmt.Println(index)
		fmt.Println(value)
	}

	mySlice = append(mySlice, 12)
	for index, value := range mySlice {
		fmt.Println(index)
		fmt.Println(value)
	}

	/*miCanal := make(chan int)
	go doSomething(miCanal)
	<-miCanal*/

	g := 25
	fmt.Println(g)

	h := &g
	fmt.Println(h)
	fmt.Println(*h)

}

func doSomething(canal chan int) {
	time.Sleep(3 * time.Second)
	fmt.Println("Listo")
	canal <- 1
}
package main

import (
	"fmt"
	"strconv"
	"time"
)

func main() {
	var x int
	x = 8
	y := 7

	fmt.Println(x)
	fmt.Println(y)

	// Handling errors
	myValue, err := strconv.ParseInt("Nan", 0, 64)
	if err != nil {
		fmt.Printf("%v\n", err)
	} else {
		fmt.Printf("%v\n", myValue)
	}

	// Map
	m := make(map[string]int)
	m["Key"] = 6
	fmt.Println(m["Key"])

	// Slice
	s := []int{1, 2, 3, 4, 5}
	for i, v := range s {
		fmt.Println(i, v)
	}

	fmt.Println()

	// Append slice
	s = append(s, 6)

	// Range
	for i, v := range s {
		fmt.Println(i, v)
	}

	fmt.Println()

	// Delete from slice
	s = append(s[:2], s[3:]...)

	// Range
	for i, v := range s {
		fmt.Println(i, v)
	}

	// Struct
	type Person struct {
		Name string
		Age  int
	}
	p := Person{"Bob", 20}
	fmt.Println(p.Name)
	fmt.Println(p.Age)

	// Pointer
	var p2 *Person
	p2 = &p
	fmt.Println(p2.Name)
	fmt.Println(p2.Age)

	fmt.Println()

	// Go routines
	c := make(chan int)
	go doSomething(c)

	fmt.Println(<-c)

	fmt.Println()

	// More pointers
	g := 25
	fmt.Println(g)
	h := &g
	fmt.Println(*h)

	*h = 30
	fmt.Println(g)

}

func doSomething(c chan int) {
	fmt.Println("Doing something")
	time.Sleep(1 * time.Second)
	fmt.Println("Done")

	c <- 1
}

exelente explicacion , me encanta este curso

Que gran repaso

Excelente repaso