Aula 33 – Tutorial Golang – Iteração sobre Valores Recebidos de um Canal
Aula 33 – Tutorial Golang – Iteração sobre Valores Recebidos de um Canal
Compartilho com vocês conteúdo de qualidade de forma gratuita como parte da missão do Código Fluente.
No entanto, também quero apresentar uma oportunidade imperdível de aprimorar suas habilidades em programação para alcançar um nível avançado.
Conheça agora mesmo o curso Master Full Stack clicando no link abaixo 👇 e confira!
Página principal do blog
Todas as aulas desse curso
Aula 32 Aula 34
Redes Sociais:
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
Lofi Music Zone Beats
Backing Track / Play-Along
Código Fluente
Putz!
Vocal Techniques and Exercises
PIX para doações
Aula 33 – Tutorial Golang – Iteração sobre Valores Recebidos de um Canal
Introdução:
Nesta aula, vamos explorar um conceito importante em programação concorrente em Go: a iteração sobre valores recebidos de um canal.
Veremos como a sintaxe do for e range pode ser utilizada para percorrer os valores recebidos de um canal e como o fechamento do canal afeta essa iteração.
package main
import "fmt"
func main() {
// Criando um canal com capacidade para 2 valores
// Iremos iterar sobre 2 valores no canal `queue`
queue := make(chan string, 2)
queue <- "one"
queue <- "two"
close(queue)
// Iterando sobre os valores recebidos do canal usando `for` e `range`
for elem := range queue {
fmt.Println(elem)
}
}
A função main é a entrada principal do programa
Nela criamos um canal chamado queue com capacidade para 2 valores usando make(chan string, 2).
Em seguida, enviamos dois valores para o canal queue usando o operador <-.
Fechamos o canal queue usando a função close(queue).
Isso indica que nenhum valor adicional será enviado para o canal.
Usamos um loop for e range para iterar sobre os valores recebidos do canal queue.
O loop continua até que o canal seja fechado e todos os valores sejam recebidos.
Em cada iteração, o valor recebido é armazenado na variável elem, e então o valor é impresso no console usando fmt.Println(elem).
O resultado será a impressão dos valores “one” e “two” que foram enviados e recebidos do canal queue.
Esse exemplo ilustra como utilizar o for e range para iterar sobre os valores recebidos de um canal em Go.
É uma forma eficiente de processar os valores recebidos do canal de forma sequencial.
Processamento Paralelo
Caso de uso
Ao usar goroutines para processamento paralelo, é comum utilizar canais para enviar e receber dados entre as goroutines.
A iteração sobre os valores recebidos de um canal permite processar os resultados simultaneamente.
Import do sync
Por causa do import do sync, antes de rodar o código precisamos executar: go mod init <modulename>, para limpar e atualizar o arquivo go.mod e o arquivo go.sum.
Ele remove as dependências que não estão mais sendo utilizadas no projeto e adiciona as dependências necessárias com as versões corretas.
É útil para garantir que as dependências do projeto estejam atualizadas e corretas.
No meu caso, vou chamar o módulo de fluentcode.com.
go mod init fluentcode.com
Rode também o: go mod tidy para inicializar um módulo e gerenciar as dependências do projeto.
go mod tidy
Exemplo de código:
package main
import (
"fmt"
"sync"
)
func processWorker(id int, jobs <-chan int, wg *sync.WaitGroup) {
defer wg.Done()
for job := range jobs {
fmt.Printf("Worker %d processing job %d\n", id, job)
// Realize o processamento do trabalho aqui
}
}
func main() {
jobs := make(chan int, 5)
var wg sync.WaitGroup
numWorkers := 3
for i := 1; i <= numWorkers; i++ {
wg.Add(1)
go processWorker(i, jobs, &wg)
}
// Enviar trabalhos para os workers
for i := 1; i <= 10; i++ {
jobs <- i
}
close(jobs)
// Aguardar a conclusão de todos os workers
wg.Wait()
}
Neste exemplo, criamos goroutines (workers) que processam trabalhos em paralelo.
Os trabalhos são enviados para o canal jobs e cada worker itera sobre os valores recebidos desse canal usando o for e range.
A saída do código será algo semelhante a:
Worker 1 processing job 1
Worker 2 processing job 2
Worker 3 processing job 3
Worker 1 processing job 4
Worker 2 processing job 5
Worker 3 processing job 6
Worker 1 processing job 7
Worker 2 processing job 8
Worker 3 processing job 9
Worker 1 processing job 10
No entanto, é importante ressaltar que a ordem exata pode variar a cada execução do programa devido à concorrência dos workers.
Portanto, embora o primeiro worker comece a processar o job 1, não é garantido que ele terminará de processá-lo antes que outros workers iniciem seus próprios jobs.
Além disso, como o canal jobs tem uma capacidade de 5, o envio de jobs além desse limite ficará em espera até que os workers processem alguns jobs existentes e liberem espaço no canal.
Portanto, a ordem precisa de processamento e a distribuição exata dos jobs entre os workers podem variar a cada execução.
Função processWorker() do código acima
A função processWorker() representa a rotina de trabalho de cada worker.
Recebe o ID do worker, um canal jobs de onde ele recebe os trabalhos a serem processados e um ponteiro para um objeto sync.WaitGroup para indicar que o worker concluiu seu trabalho.
O loop: for job := range jobs, percorre o canal jobs e recebe os trabalhos à medida que são enviados.
Em seguida, o worker processa o trabalho, que é simulado com a impressão de uma mensagem.
O defer wg.Done() é usado para indicar que o worker terminou seu trabalho, decrementando o contador do sync.WaitGroup quando a função processWorker retorna.
Função main() do código acima
A função main() é responsável por configurar e coordenar os workers.
Criamos um canal jobs com uma capacidade de 5 para armazenar os trabalhos a serem processados.
Inicializamos uma variável wg do tipo sync.WaitGroup para sincronizar a finalização de todos os workers.
Definimos o número de workers desejado (numWorkers = 3) e, em seguida, iniciamos goroutines chamando a função processWorker() para cada worker.
Em seguida, enviamos 10 trabalhos para o canal jobs utilizando um loop for.
Fechamos o canal jobs para indicar que nenhum trabalho adicional será enviado.
Por fim, chamamos wg.Wait() para aguardar a conclusão de todos os workers, o que é indicado pelo wg.Done() em cada worker.
Consumo de Streams de Dados
Caso de uso
Em situações em que os dados estão sendo transmitidos continuamente em um canal, a iteração sobre os valores recebidos permite consumir esses dados conforme eles estão disponíveis, em tempo real.
Exemplo de código:
package main
import (
"fmt"
"time"
)
func dataProducer(data chan<- int) {
for i := 1; i <= 5; i++ {
data <- i
time.Sleep(time.Second)
}
close(data)
}
func main() {
data := make(chan int)
go dataProducer(data)
for value := range data {
fmt.Println("Received:", value)
}
}
Neste exemplo, temos uma goroutine dataProducer que envia valores para o canal data em intervalos de 1 segundo.
A função main() itera sobre os valores recebidos desse canal usando o for e range, imprimindo os valores à medida que são recebidos.
Esses são apenas dois casos de uso comuns em que a iteração sobre valores recebidos de um canal é aplicável.
A versatilidade dessa abordagem permite seu uso em uma ampla gama de cenários de programação concorrente.
Conclusão
Dominar a iteração sobre valores recebidos de um canal é essencial para o desenvolvimento de programas concorrentes eficientes em Go.
Ao compreender os conceitos abordados nesta aula, você estará um passo mais próximo de aproveitar todos os benefícios da programação concorrente em Go.
Continue praticando e explorando os recursos dessa poderosa linguagem!
#Golang #ProgramacaoConcorrente #IteracaoCanal #ForRange