Channels en Go (Golang)

in #cervantes5 years ago

gopher wallpaper

Ahora que conocemos las goroutines y el WaitGroup es momento de hablar de los channels, un tipo de dato muy ligado a las Goroutines y a la concurrecia en Go

Channels

Un channel es un tipo de variable que permite el paso de información entre gorourines. Me parece la manera más sencilla de definirlo. Aunque, es posible que no quedara del todo claro, así que veamos a los channels en acción.

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func imprimir(ch chan string, texto string) {
    for {
        // Pasando información al channel.
        ch <- texto

        // Tiempo de espera entre cada iteración.
        // rand.Int nos permite generar un entero de manera aleatoria
        // time.Sleep requiere que la variable sea de tipo time.Duration
        time.Sleep(time.Duration(rand.Int()) * time.Nanosecond)
    }
}

func main() {
    // Creando un channel de tipo string.
    ch := make(chan string)

    // Pasando el channel a dos goroutines.
    go imprimir(ch, "primera")
    go imprimir(ch, "segunda")

    // Ciclo infinito que imprime la información que viene en el channel.
    for {
        fmt.Println(<-ch)
    }
}



Comencemos por la declaración del channel ch := make(chan string), hacemos uso de la función make como hemos visto antes con los maps y los slices. Pero no declaramos un channel simplemente, especificamos además el tipo de variable que va a manejar, en este caso es de tipo string y este será el único tipo de dato que aceptará el channel. Sin embargo, es posible crear channels de cualquiera de los tipos vistos anteriormente, incluyendo las estructuras.

La llamada a la función imprimir no aporta nada realmente nuevo, usamos la palabra reservada go para crear una nueva goroutine y le pasamos el channel recientemente creado y una cadena de texto. Finalmente mandamos a imprimir el contenido del channel dentro de un ciclo infinito.

En este punto si podemos notar algo interesante, ese operador semejante a una flecha <-ch. De esta forma extraemos datos del channel. Si ahora observamos la función imprimir, encontramos un caso semejante: ch <- texto. Esta vez no extraemos información del channel sino que la introducimos al mismo.

Channels de una dirección

Un detalle interesante sobre los channels es que podemos establecer que sólo trabajen en un sentido, es decir, que exclusivamente reciban datos o los envíen. Modificando un poco la función imprimir, podemos ver la sintaxis requerida para este fin.

...
func imprimir(ch chan<- string, texto string) {
    for {
        ch <- texto
        time.Sleep(time.Duration(rand.Int()) * time.Nanosecond)
    }
}
...



Apenas se puede notar la diferencia, así que les diré donde deben mirar: chan<-. La flecha apuntando al la palabra reservada chan indica que este channel sólo puede recibir información. Esto hace fácil deducir la sintaxis utilizada para establecer un channel que envía información: <-chan.

Cerrando un channel

Podemos cerrar un channel haciendo uso de la función close, pasando como argumento el channel a cerrar. Por ejemplo:

...
func imprimir(ch chan<- string, texto string) {
    for i := 0; i < 5; i++ {
        ch <- texto
        time.Sleep(time.Duration(rand.Int()) * time.Nanosecond)
    }
    close(ch)
}
...


Podemos ver que esta vez el ciclo es finito, por lo que se imprimirá 5 veces cada palabra antes de cerrar el ch por medio de la función close. Sin embargo, al ejecutarlo notarán que posterior a las 5 impresiones comenzarán a imprimir cadenas vacías. Finalmente, obtenemos un mensaje como esta: panic: close of closed channel.

La razón es que aunque ya está cerrado el channel, seguímos solicitando información, y este nos envía el valor cero del tipo de dato, en este caso, una cadena vacía. Respecto al panic, ya que estamos ejecutando la función imprimir en dos goroutines diferentes, la primera en concluir cerrará el channel, la otra intentará hacer los mismo, pero debido a que el channel ya se encuentra cerrado se produce el panic.

Confirmando si un channel se encuentra cerrado

Si queremos evitar que sucedan casos como el anterior, sería conveniente confirmar si un channel se encuentra cerrado o no antes de trabajar con él. Para este fin, podemos recurrir al segundo valor obtenido al extraer valores de un channel, un boolean que indica si el channel se encuentra abierto. Su sintaxis es la siguiente: valor, booleano := <- channel. Modifiquemos el código anterior.

...
func main() {
    ch := make(chan string)

    go imprimir(ch, "primera")
    go imprimir(ch, "segunda")
    for {
        valor, ok := <-ch
        if !ok {
            break
        }
        fmt.Println(valor)
    }
}



Ahora confirmamos si el channel se encuentra cerrado y terminamos el ciclo. Con esto la ejecución del programa finaliza una vez que el channel se encuentra cerrado. Otra manera de hacer esto, y una que me gusta más, es usar el for-range.

...
for valor := range ch {
    fmt.Println(valor)
}
...


separator.png

Publicaciones relacionadas

  1. De Python a Go (Golang)

  2. Introducción al lenguaje de programación Go (Golang)

  3. Estructuras de control de flujo en Go

  4. Array y Slice en Go (Golang)

  5. Maps en Go (Golang)

  6. Punteros en Go (Golang)

  7. Importaciones y paquetes en Go (Golang)

  8. Paquetes de terceros y alias en Go (Golang)

  9. Tipos de datos Personalizados y Métodos en Go (Golang)

  10. Estructuras: ¿Las Clases de Go (Golang)?

  11. Interfaces en Go (Golang)

  12. Interfaces vacías en Go (Golang)

  13. Manejo de errores en Go (Golang)

  14. Conociendo Panic y Recover en Go (Golang)

  15. Conociendo las gorutinas en Go (Golang)

  16. Goroutines y el WaitGroup en Go (Golang)

Gracias por leer, espero que este artículo te resultara de provecho. Si así fue, no dudes en dejar un comentario, compartirlo y votar. Te invito a comentar cualquier duda o sugerencia, te aseguro que las leo todas. Así que, por favor, ayúdame a mejorar y continuar compartiendo contenido de calidad. Si te gusta la programación y/o la informática en general, te invito a formar parte de la comunidad Develop Spanish dónde compartimos contenido de esa naturaleza y totalmente en español. Hasta la próxima.

banner-steemit.jpg

Sort:  

Hello! Your post has been resteemed and upvoted by @ilovecoding because we love coding! Keep up good work! Consider upvoting this comment to support the @ilovecoding and increase your future rewards! ^_^ Steem On!

Reply !stop to disable the comment. Thanks!

Congratulations @orlmicron! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :

You made more than 600 upvotes. Your next target is to reach 700 upvotes.

Click here to view your Board of Honor
If you no longer want to receive notifications, reply to this comment with the word STOP

Do not miss the last post from @steemitboard:

Saint Nicholas challenge for good boys and girls

Support SteemitBoard's project! Vote for its witness and get one more award!

Congratulations @orlmicron! You received a personal award!

Happy Birthday! - You are on the Steem blockchain for 1 year!

You can view your badges on your Steem Board and compare to others on the Steem Ranking

Do not miss the last post from @steemitboard:

The Steem blockchain survived its first virus plague!
Vote for @Steemitboard as a witness to get one more award and increased upvotes!

Coin Marketplace

STEEM 0.26
TRX 0.11
JST 0.032
BTC 63510.75
ETH 3065.54
USDT 1.00
SBD 3.82