Aula 26 – Tutorial Golang – Channels

Aula 26 – Tutorial Golang – Channels

Tutorial Golang - Channels

Tutorial Golang – Channels

Pacote Programador Fullstack

Pacote Programador Fullstack

Página principal do blog

Todas as aulas desse curso

Aula 25                        Aula 27

Redes Sociais:

facebook

Meus links de afiliados:

Hostinger

Digital Ocean

One.com

Melhore seu NETWORKING

https://digitalinnovation.one/

Participe de comunidades de desenvolvedores:

Fiquem a vontade para me adicionar ao linkedin.

E também para me seguir no https://github.com/toticavalcanti.

Código final da aula:

https://github.com/toticavalcanti

Canais do Youtube:

Toti

Backing Track / Play-Along

Código Fluente

Putz!

Vocal Techniques and Exercises

PIX para doações

PIX Nubank

PIX Nubank

Aula 26 – Tutorial Golang – Channels

Channels

Canais são tubos que conectam goroutines dentro de seus aplicativos Go, que permitem a comunicação e posteriormente, a passagem de valores de, e para variáveis.

Eles são incrivelmente úteis e podem ajudá-lo a criar aplicativos de desempenho incrivelmente alto e com muitas rotinas concorrentes em Go com o mínimo esforço em comparação com outras linguagens de programação.

Isso não foi um acaso, ao projetar a linguagem Go, os principais desenvolvedores decidiram que queriam simultaneidade dentro da linguagem e torná-la o mais simples possível deixando para os desenvolvedores, a liberdade para trabalhar no que é realmente mais importante.

Teoria

A ideia de canais não é nada nova, pois como muitos dos recursos de simultaneidade, isto é, rotinas que acontecem em paralelo, esses conceitos foram trazidos de nomes como Hoare’s Communicating Sequential Processes (1978), CSP para abreviar, e até mesmo de Guarded Command Language (GCL)( 1975).

A Guarded Command Language ( GCL ) é uma linguagem definida por Edsger Dijkstra para a semântica do transformador de predicado.

Ele combina conceitos de programação de uma forma compacta, antes que o programa seja escrito em alguma linguagem de programação prática.

Sua simplicidade facilita a comprovação da correção de programas, utilizando a lógica de Hoare .

Os desenvolvedores do Go, no entanto, têm como missão apresentar esses conceitos da maneira mais simples possível para permitir que os programadores criem aplicativos melhores, mais corretos e altamente simultâneos.

Um Exemplo Simples

Vamos começar com um exemplo simples de como channels funciona.

Vamos criar uma função que vai calcular um valor aleatório arbitrário e passar de volta para uma variável do tipo channel chamada value:


package main

import (
    "fmt"
    "math/rand"
)

func CalculateValue(values chan int) {
    fmt.Println(time.Now().UnixNano())
    rand.Seed(time.Now().UnixNano())
    value := rand.Intn(10)
    fmt.Println("Calculated Random Value: {}", value)
    values <- value
}

func main() {

    values := make(chan int) // create unbuffered channel of integers
    //deferred the closing of our channel until the end of our main() function’s execution
    defer close(values)
    // start single goroutine passing in our newly created values channel as its paramete
    go CalculateValue(values)
    // receives a value from our values channel.
    value := <-values  
    fmt.Println(value)
}

Vamos dissecar o código.

Em nossa função main(), declaramos values := make(chan int)

Essa declaração cria o canal para que possamos usá-lo posteriormente na goroutine CalculateValue.

Nota – Usamos make ao instanciar nosso canal de values, pois, como maps e slices, os channels devem ser criados antes do uso.

Depois de criarmos o canal, chamamos defer close(values) que adia o fechamento do nosso canal até o final da execução da função main().

Essa geralmente é considerada a melhor prática.

Após nossa chamada para adiar, iniciamos nossa única goroutine: CalculateValue(values) passando nosso canal de values como parâmetro.

Dentro de nossa função CalculateValue(), calculamos um único valor aleatório entre 1-10, imprimimos isso e enviamos esse valor para nosso canal de valores chamando value := <-values.

Após a execução deste código, você deverá ver a saída parecida com esta:

Saída:
Calculated Random Value: {} 7

Exemplo 2

Instanciar e usar canais em seus programas Go parece bastante simples até agora, mas, e em cenários mais complexos?

Unbuffered Channels

Usar um canal tradicional nas goroutines, às vezes pode levar a problemas de comportamento inesperados da aplicação.

Com canais tradicionais sem buffer, sempre que uma goroutine envia um valor para este canal, essa goroutine bloqueará posteriormente até que o valor seja recebido do canal.

Vejamos isso em um exemplo real.

Se dermos uma olhada no código abaixo, é muito semelhante ao código que tínhamos anteriormente.

No entanto, estendemos nossa função CalculateValue() para executar um fmt.Println(), depois de enviar seu valor calculado aleatoriamente para o canal.

Em nossa função main(), adiciona uma segunda chamada ao CalculateValue(valueChannel), portanto, devemos esperar 2 valores enviados para este canal em uma sucessão muito rápida.


package main

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

func CalculateValue(c chan int) {
    fmt.Println(time.Now().UnixNano())
    rand.Seed(time.Now().UnixNano())
    value := rand.Intn(10)
    fmt.Println("Calculated Random Value: {}", value)
    time.Sleep(1000 * time.Millisecond)
    c <- value
    fmt.Println("Only Executes after another goroutine performs a receive on the channel")
}

func main() {

    valueChannel := make(chan int)
    defer close(valueChannel)

    go CalculateValue(valueChannel)
    go CalculateValue(valueChannel)

    values := <-valueChannel
    fmt.Println(values)
}

No entanto, quando você executa isso, deve aparecer apenas a instrução final de impressão da nossa primeira goroutines que é realmente executada:

Saída:
Calculated Random Value: {} 1
Calculated Random Value: {} 7
1
Only Executes after another goroutine performs a receive on the channel

A razão para isso é que nossa chamada para c <- value foi bloqueada em nossa segunda goroutine e posteriormente, a função main() conclui sua execução antes que nossa segunda goroutine tivesse a chance de concluir sua própria execução.

Buffered Channels

A maneira de contornar esse comportamento de bloqueio é usar algo chamado de canal em buffer.

Esses canais em buffer são essencialmente filas de um determinado tamanho que podem ser usadas para comunicação entre gorotines.

Para criar um canal com buffer em oposição a um canal sem buffer, fornecemos um argumento de capacidade para nosso comando make:


bufferedChannel := make(chan int, 3)

Alterando isso para um canal com buffer, nossa operação de envio, c <- value apenas bloqueia dentro das goroutines se o canal estiver cheio.

Vamos modificar nosso programa existente para usar um canal em buffer e dar uma olhada na saída.

Observe que foi adicionada uma chamada para time.Sleep() na parte inferior de nossa função main() para bloqueio preguiçoso(lazily block) na função main(), o suficiente para permitir que nossas goroutines concluam a execução.


package main

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

func CalculateValue(c chan int) {
    fmt.Println(time.Now().UnixNano())
    rand.Seed(time.Now().UnixNano())
    value := rand.Intn(10)
    fmt.Println("Calculated Random Value: {}", value)
    time.Sleep(1000 * time.Millisecond)
    c <- value
    fmt.Println("This executes regardless as the send is now non-blocking")
}

func main() {

    valueChannel := make(chan int, 2)
    defer close(valueChannel)

    go CalculateValue(valueChannel)
    go CalculateValue(valueChannel)

    values := <-valueChannel
    fmt.Println(values)

    time.Sleep(1000 * time.Millisecond)
}

Agora, quando executamos isso, devemos ver que nossa segunda goroutine de fato continua sua execução, independentemente do fato de um segundo recebimento não ter sido chamado em nossa função main().

Graças ao time.Sleep(), podemos ver claramente a diferença entre canais sem buffer e com buffer, e sua natureza de não bloqueio quando não cheios.

Saída:
Calculated Random Value: {} 1
Calculated Random Value: {} 7
7
This executes regardless as the send is now non-blocking
This executes regardless as the send is now non-blocking 

É isso pessoal, fico por aqui!

Até mais. 🙂

Página principal do blog

Todas as aulas desse curso

Aula 25                        Aula 27

Meus links de afiliados:

Hostinger

Digital Ocean

One.com

Obrigado e bons estudos. 😉

About The Author
-

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>