Tutorial de canales Go

En este tutorial, veremos cómo puede usar canales dentro de sus aplicaciones basadas en Go.

Los canales son tuberías que enlazan entre goroutines dentro de sus aplicaciones basadas en Go que permiten la comunicación y, posteriormente, el paso de valores hacia y desde variables.

Son increíblemente prácticos y pueden ayudarlo a crear aplicaciones altamente concurrentes de alto rendimiento en Go con un mínimo esfuerzo en comparación con otros lenguajes de programación. Esto no fue de ninguna manera una casualidad, al diseñar el idioma, los desarrolladores principales decidieron que querían que la concurrencia dentro de su idioma fuera un ciudadano de primera clase y que fuera tan fácil trabajar con él como fuera posible, sin ir demasiado lejos y sin permitir a los desarrolladores la libertad en la que necesitan trabajar.

La capacidad de crear sistemas concurrentes tan fácilmente es algo que me atrajo al lenguaje en primer lugar, y tengo que decir que ha sido una luz absoluta hasta ahora.

Nota: Le recomendaría echar un vistazo a mi otro tutorial sobre goroutines si desea aprender más sobre goroutines.

Objetivos

Al final de este tutorial,:

  • Tener una sólida comprensión de la teoría detrás de los canales
  • Ser capaz de crear aplicaciones de Go simultáneas simples que utilicen canales

Requisitos previos

Para completar este tutorial, deberá haber cumplido los siguientes requisitos:

  • Necesitará Ir instalado en su máquina.

Tutorial en vídeo

Si lo desea, este tutorial está disponible en formato de vídeo.

La Teoría

La idea de canales no es nada nuevo, ya que, como muchas de las características de concurrencia de Go, estos conceptos se han desarrollado a partir de Procesos secuenciales Comunicantes de Hoare (1978), CSP para abreviar, e incluso de las figuras de comandos guardados de Dijkstra (1975).

Los desarrolladores de Go, sin embargo, han hecho su misión presentar estos conceptos de una manera simple como sea posible para permitir a los programadores crear aplicaciones mejores, más correctas y altamente concurrentes.

Un ejemplo simple

Comencemos por ver cómo podemos construir un ejemplo realmente simple de cómo funciona en Go. Primero crearemos una función que desaparece y calcula un valor aleatorio y lo pasa de vuelta a una variable de canal llamadavalues:

principal.vamos

Vamos a diseccionar lo que pasó aquí. En nuestra función main(), llamamos avalues := make(chan int), esta llamada creó efectivamente nuestro nuevo canal para que posteriormente pudiéramos usarlo dentro de nuestra CalculateValue gorroutine.

Nota: Usamos make al crear instancias de nuestro canal values como, mapas de similitud y segmentos, los canales deben crearse antes de usarlos.

Después de crear el canal, llamamos a defer close(values), lo que impidió el cierre de nuestro canal hasta el final de la ejecución de nuestra función main(). Por lo general, esto se considera una buena práctica para garantizar que ordenamos después de nosotros mismos.

Después de nuestra llamada a defer, iniciamos nuestra única goroutine:CalculateValue(values) pasando a nuestro canal recién creado values como su parámetro. Dentro de nuestra función CalculateValue, calculamos un único valor aleatorio entre 1-10, lo imprimimos y luego enviamos este valor a nuestro canal valuesllamando a values <- value.

Volviendo a nuestra función main(), llamamos a value := <-values, que devuelve un valor de nuestro canal values.

Nota-Observe cómo cuando ejecutamos este programa, no termina inmediatamente. Esto se debe a que el acto de enviar y recibir desde un canal está bloqueando. Nuestra función main() se bloquea hasta que recibe un valor de nuestro canal.

Al ejecutar este código, debería ver que la salida se ve algo como esto:

$ go run main.goGo Channel TutorialCalculated Random Value: {} 77

Resumen:

myChannel := make(chan int) – crea myChannel, que es un canal de tipoint

channel <- value – envía un valor a un canal

value := <- channel – recibe un valor de un canal

Por lo tanto, la creación de instancias y el uso de canales en sus programas Go parece justo hasta ahora, pero ¿qué pasa en escenarios más complejos?

Canales sin búfer

El uso de un channel tradicional dentro de sus goroutines a veces puede generar problemas con un comportamiento que puede no estar esperando. Con canalesunbuffered tradicionales, cada vez que un goroutine envía un valor a este canal,ese goroutine se bloqueará posteriormente hasta que el valor se reciba del canal.

Veamos esto en un ejemplo real. Si echamos un vistazo al siguiente código, es muy similar al código que teníamos anteriormente. Sin embargo, hemos ampliado nuestra funciónCalculateValue() para realizar un fmt.Println después de enviar su valor calculado al canal.

En nuestra función main(), hemos agregado una segunda llamada ago CalculateValue(valueChannel), por lo que debemos esperar que se envíen 2 valores a este canal en una sucesión muy rápida.

principal.ir

Sin embargo, cuando ejecute esto, debería ver que solo se ejecuta la primera sentencia finalprint de goroutines:

go run main.goGo Channel TutorialCalculated Random Value: {} 1Calculated Random Value: {} 71Only Executes after another goroutine performs a receive on the channel

La razón de esto es que nuestra llamada a c <- value se ha bloqueado en nuestra segunda línea y, posteriormente, la función main() concluye su ejecución antes de que nuestro segundo goroutine tenga la oportunidad de completar su propia ejecución.

Canales en búfer

La forma de evitar este comportamiento de bloqueo es usar algo llamado canal abuffered. Estos canales en búfer son esencialmente colas de un tamaño determinado que se pueden utilizar para la comunicación entre líneas. Para crear un canal con búfer en lugar de un canal sin búfer, suministramos un argumento de capacidad a nuestro comando make :

bufferedChannel := make(chan int, 3)

Al cambiar esto a un canal en búfer, nuestra operación de envío, c <- value solo bloquea dentro de nuestras goroutines en caso de que el canal esté lleno.

Modifiquemos nuestro programa existente para usar un canal en búfer y echemos un vistazo a la salida. Observe que he agregado una llamada a time.Sleep() en la parte inferior de nuestra funciónmain() para bloquear perezosamente nuestra función main() lo suficiente como para permitir que nuestras gorroutines completen la ejecución.

principal.go

Ahora, cuando ejecutamos esto, deberíamos ver que nuestra segunda goroutine continúa su ejecución independientemente del hecho de que una segunda recepción no se haya llamado en nuestra función main(). Gracias al time.Sleep(), podemos ver claramente la diferencia entre los canales sin búfer y su naturaleza de bloqueo y nuestros canales con búfer y su naturaleza de no bloqueo (cuando no están llenos).

Go Channel TutorialCalculated Random Value: {} 1Calculated Random Value: {} 77This executes regardless as the send is now non-blockingThis executes regardless as the send is now non-blocking

Conclusión

Así que, en este tutorial bastante largo, nos las arreglamos para aprender sobre los diferentes tipos de canales dentro de Go. Descubrimos las diferencias entre los canales con y sin búfer y cómo podríamos usarlos en nuestro beneficio dentro de nuestros programas go simultáneos.

Si te ha gustado este tutorial, no dudes en hacérmelo saber en la sección de comentarios a continuación. Si tiene alguna sugerencia sobre lo que podría hacer mejor, ¡me encantaría escucharlas en la sección de comentarios a continuación!

Más información

Si disfrutó de este artículo y desea obtener más información sobre cómo trabajar con Concurrencyin Go, le recomiendo que consulte nuestros otros artículos sobre concurrencia:

  • Tutorial Go Mutex
  • Tutorial Go Goroutines
  • Go sync.Tutorial de Grupo de espera

Deja una respuesta

Tu dirección de correo electrónico no será publicada.

Previous post Sistema Bibliotecario del Condado de Brunswick
Next post ¿Debería Pintar o Teñir gabinetes? Pros y Contras de Cada