En el fascinante mundo de la programación concurrente con Go, los channels son elementos clave que facilitan la comunicación entre goroutines. Imagínalos como conductos que permiten el paso de datos de un goroutine a otro de manera eficiente. Este concepto es crucial para aquellos que buscan optimizar su código y manejar tareas concurrentes de manera efectiva. A medida que profundicemos en los channels, descubrirás cómo pueden mejorar tus aplicaciones, proporcionándote herramientas para gestionar datos de manera optima.
¿Cómo se crean los channels en Go?
La creación de channels en Go es un proceso bastante directo. Usamos el keyword make para inicializarlos, estableciendo el tipo de dato que el canal manejará. A continuación se muestra el uso básico para crear un canal:
c :=make(chanstring)
Aquí, se declara un channel que manejará datos de tipo string. Es importante señalar que podemos definir cuántos datos simultáneos manejará el canal en su creación. Aunque opcional, especificar este límite mejora la optimización y es considerado una buena práctica en Go.
¿Cómo se utiliza un channel dentro de una función?
Emplear channels dentro de funciones permite gestionar entradas y salidas de datos entre goroutines. Dentro de una función, se puede definir un argumento de tipo channel, y especificar si será para entrada o salida de datos.
funcenviarTexto(texto string, c chan<-string){ c <- texto
}
En este ejemplo, chan<- string indica que el canal c es de solo entrada, optimizando el entendimiento y rendimiento del código. Esta anotación ayuda a evitar errores y mejoran la claridad del flujo de datos.
¿Cómo extraemos datos de un channel?
Una vez que los datos son enviados a través de un channel, deben ser extraídos adecuadamente para asegurar que una goroutine receptor los procese correctamente. El siguiente fragmento de código demuestra cómo se puede recibir y mostrar un mensaje desde un channel:
texto :=<- c
fmt.Println(texto)
Aquí, el operador <- es utilizado a la izquierda del canal c para recibir el dato enviado y asignarlo a la variable texto. Este uso preciso de operadores asegura que las goroutines trabajen en armonía, intercambiando datos eficientemente.
¿Por qué especificar la dirección de un channel mejora el código?
Con Go, es recomendable definir si un channel es de entrada o salida para cada parámetro de una función, ya que esto:
Mejora la claridad: El flujo de datos se hace más evidente, facilitando la comprensión del código.
Optimiza la eficiencia: Al limitar los canales a sus roles específicos, ayudas al compilador a optimizar el código.
Reduce Errores: Minimiza la probabilidad de errores relacionados con la dirección incorrecta de transmisión de datos.
Recomendaciones para el uso de channels
Claridad y Simplicidad: Siempre identifica claramente si el canal es de entrada o salida.
Optimización Inteligente: Solo elimínalos si la eficiencia es crítica, de lo contrario, los channels son la opción más manejable.
Usa casos Prácticos: Evalúa cuándo realmente se justifica utilizar channels para facilitar el manejo de concurrencia.
La concurrencia en Go abre las puertas a un manejo robusto de tareas complejas, y los channels se presentan como una herramienta indispensable en este viaje. Anímate a explorar más sobre Go y la concurrencia, y comenzarás a ver mejoras significativas en cómo tus aplicaciones manejan múltiples tareas simultáneamente. 📚🔧✨
Deberían de rehacer el curso, el instructor le da muchas vueltas y eso confunde bastante, también que hable más claro
Qu'e no te ha quedado claro?
Tal vez deberias tomar primero el curso de programacion basica o el curso de programacion con Python donde el profesor se detiene a explicar muchas cosas que aqui se dan por sentadas porque se asume que las sabes de cursos de preparacion.
la verdad no es muy clara la explicación, es mejor leer la documentación
El curso nuevo es peor jaja
Creo que haciendo un proyecto e implementando channels es cuando en verdad vamos a aprender a usarlos, espero y en los proximos cursos de go de niveles más altos hagamos eso
Goroutines sería útil a la hora de procesar documentos o imágenes.
Es muy buena clase e instructor, pero esto para nada es básico, lleva de fondo mucho contexto y no es explicado, sería bueno retomarlo en otro curso, pero desde cero
Entre las aplicaciones de las go routines, me vienen a la cabeza estas:
Procesamiento de archivos por lotes (cada lote sería un proceso distinto).
Algoritmos en los que pueda aprovechar paralelismo, como la búsqueda bidireccional.
Simulaciones que aprovechen la concurrencia, por ejemplo, un modelo estadístico de la pandemia (cada region sería un proceso diferente)
Entrenamiento de agentes con aprendizaje por refuerzos. Por ejemplo, agentes que compiten en un entorno. Cada agente podría ser un proceso y el entorno en sí mismo también.
package main
import"fmt"func say(text string, c chan<- string){ c <- text
}func main(){c:=make(chan string,1) fmt.Println("Hello") go say("Bay", c) fmt.Println(<-c)}
Facilmente podrían crear un curso enfocado sólo en concurrencia en Go, o en channels, así como por ejemplo hay un curso completo de asincronismo en JS.
¿Por qué es menos eficiente usar los channels que las go routines?
Probablemente se expresó mal; de hecho los channels son necesarios y muy importantes para que nuestras go routines pueden comunicarse entre si, es decir, se puedan sincronizar o distribuir su trabajo para que asi tengamos un resultado final esperando que terminen las goroutines su ejecución y no que una termine abruptamente sin esperar a las demas. Tambén pueden haber casos donde queremos evitar problemas de condiciones de carrera (para ciertos tipos de ejecuciones donde el orden de ejecución importa, como por ejemplo que el resultado de una go routine sea necesario para otra go routine).
También cabe mencionar que es importante profundizar un poco en el tema porque una mal implementación podria traer como problemas como un deadlock que como si nombre lo indica pueden bloquearse los recursos en una go routine y por ende quedarse bloqueado nuetro programa.
Te recomiendo leer un poco mas, por acá hay un pequeño intro al tema con ejemplos y avanzar con channels con buffer que te permite ademas tener una especie de memoria con los channels. Esto es todo un tema, y muy interesante.
Saludos
me pueden dar un ejemplo práctico (para el uso de channel)?
El loop del main funciona como un listener
package main
import("fmt""time")func timeLoop(c chan int64){fori:=0; i <=10000; i++{ c <-int64(i) time.Sleep(time.Second*1)}}func main(){c:=make(chan int64,1) go timeLoop(c)for{ fmt.Println(<-c)}}
Me gusto tu ejemplo, asi que le hice unas mejoras para visualizar claramente el envío y recepción de los valores:
package main
import("fmt""time")func timeLoop(c chan int64){fori:=0; i <=10; i++{ fmt.Println("Sending value", i) c <-int64(i) time.Sleep(time.Second*1)}close(c)}func main(){c:=make(chan int64,1) fmt.Println("Starting") go timeLoop(c)forvalue:= range c { fmt.Println("Received value", value)} fmt.Println("End TimeLoop")}
faltó un ejemplo sobre usar los channels de salida.
Este podría ser un ejemplo para imprimir la salida del Channel:
package main
import"fmt"func say(text string, c chan<- string){// canal de entrada de datos c <- text
}func printChannelOutput(c <-chan string){// canal de salida de datosvar output string
output =<-c
fmt.Println(output)}func main(){// c := make(chan string) // dinamically accepts goroutinesc:=make(chan string,1)// one goroutine at time fmt.Println("Hello") go say("Bye", c)//fmt.Println(<-c)printChannelOutput(c)}
Se puede comparar la concurrencia vs. asincronismo?
Este Articulo talvez te pueda interesar.
Estamos hablando de dos niveles diferentes, en la programación, concurrencia hace referencia a procesos ejecutándose en paralelo y asincronismo de javascript se refiere a un proceso ejecutando hilos en paralelo.
¿Pueden colocar unos ejemplos mas claros y practicos al momento de utilizar loz Channels ya que solo con mprimir un Hola o un Bye no queda claro para que utilizar lo y la diferencia entre Channel y los Goroutines?
Si le asigno que voy a manejar 1 elemento a la vez de ese tipo de dato, y lo llego a superar. Que comportamiento tendria? Dinamicamente aumenta?
Si le asignas la cantidad de datos y la superas, te saldrá error, para que aumente dinámicamente no debes asignar la cantidad de datos.
Un ejemplo para los que no entendieron y se preguntan para que sirven las go routines y los channels.
Imaginemos que tenemos una funcion que suma el contenido de un array y tienes un array muy muy grande de enteros entonces para solucionarlo usamos las go routines, partimos en 2 el array muy grande y una mitad va a una go routine y la otra mitad a otra go routine.
Cuando terminan las goroutines estas no retornan un resultado ahi es cuando entran los channels a ayudarnos, los channels nos van a servir para obtener el resultado de las sumas de cada mitad del array.
package main
import("fmt")func sumarArrays(fragmentoArray []int, c chan<- int){var suma =0forv:= range fragmentoArray { suma+=v
} c <- suma
}func main(){//instanciamos el canalch:=make(chan int)//Arreglo con un monton de numeros (puse pocos)arregloGigante:=[]int{0,1,2,3,4,5,6}//partimos en 2 el array y que se sumenvar mitad =len(arregloGigante)/2 go sumarArrays(arregloGigante[:mitad], ch) go sumarArrays(arregloGigante[mitad:], ch) fmt.Println(result)//obtenemos los resultados del canalvar resultado1 =<-ch
var resultado2 =<-ch
//calculamos el resultadovar resultado = resultado1 + resultado2
fmt.Println(resultado)}```Esta clase tiene muchas cosas interesantes que se puede sacar casi 3 o 4 cursos hablando sobre ese tema.
Soy programador con 4 años de experiencia y en esta clase es la primera vez que noto que queda muy ambiguo el tema de channels mas que nada supongo porque los ejemplos no dan mucho sentido a lo previo esto veo que pasa mucho con temas de asincronia como sucede con javascript cuando se explica promesas, call back creo en la documentación esta mejor explicado este tema
Segun lo que entiendo, despues de haber buscado por otras fuentes, la capacidad del canal (el segundo parametro del make), va a depender mucho de como quieras manejar tu programa. La capacidad se puede entender como si fuera una cola de espera, son la cantidad maxima de datos que pueden estar esperando al mismo tiempo dentro del canal hasta que el "receptor" que es el que maneja esos datos, se libere. Aca hay un ejemplo de como lo entendi yo:
En este caso nuestra capacidad maxima es de 5 y estoy enviando con un bucle un maximo de 10 datos para poder comprender el funcionamiento del canal y su capacidad.
La funcion cap(c) nos indica la capacidad del canal, que como dije antes fue seteada en 5 y como ya sabemos len(c) nos indica la cantidad de datos que tiene el canal.
En la imagen se puede apreciar como en cada vuelta se van "reemplazando" los datos hasta que la cantidad de datos en espera se vaya reduciendo a medida que el receptor los obtiene.
Vi este video en uno de los comentarios : y me sirvio mucho junto al apoyo de chatgpt para entender mejor.
Espero sirva para comprender un poco mas como funciona la capacidad del canal y cuanto poner.
Creo que como base del tema es mas o menos suficiente, pero seria mucho mas util un ejemplo mas aplicado a la realidad,
Porque asi no me queda muy claro cuando podria usarlos y su beneficio, si ingresamos info al channel, pero como se comparten entre gorutines
Estos temas hay que profundizarlos mas creo, me compre una ultima edicion de un libro que se los recomiendo explica a detalle todo ponen ejercicios basicos pero les explica hasta el punto y coma que al guardar se los quita xD: (PROGRAMACION EN GO de Mario Macias Lloret), despues de esto lo terminare porque me quedan dudas en muchos temas esperaba un poco mas del curso las explicaciones no se entienden casi le falta mas de pedagogia para explicar bien. pero sigamos