Aula 37 – Tutorial Golang – WaitGroups

Aula 37 – Tutorial Golang – WaitGroups

Tutorial Golang

Tutorial Golang

Página principal do blog

Todas as aulas desse curso

Aula 36                        Aula 38

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

Lofi Music Zone Beats

Backing Track / Play-Along

Código Fluente

Putz!

Vocal Techniques and Exercises

PIX para doações

PIX Nubank

PIX Nubank


 

Aula 37 – Tutorial Golang – WaitGroups

Introdução

As WaitGroups são uma ferramenta poderosa em Go para sincronizar goroutines e controlar o fluxo de execução em programas concorrentes.

Elas são úteis quando você precisa garantir que todas as goroutines concluam suas tarefas antes que o programa principal termine ou antes de prosseguir para a próxima etapa.

Nesta aula, vamos explorar WaitGroups e aprender a usá-las com exemplos práticos e casos de uso.

O que é uma WaitGroup?

Uma WaitGroup é uma estrutura de dados fornecida pela biblioteca padrão de Go que permite esperar que um conjunto de goroutines seja concluído antes de continuar a execução do programa principal.

Ela faz isso mantendo uma contagem interna de goroutines e bloqueando o programa principal até que todas as goroutines tenham sinalizado que terminaram.

Exemplo 1: WaitGroup Básica

Vamos começar com um exemplo simples para entender como uma WaitGroup funciona.


package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup

    // Criando três goroutines simulando tarefas independentes
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            fmt.Printf("Goroutine %d executando\n", id)
        }(i)
    }

    // Aguardando todas as goroutines concluírem
    wg.Wait()
    fmt.Println("Todas as goroutines concluídas")
}

Importações:

O programa importa as bibliotecas “fmt” e “sync” para impressão de saída e manipulação de sincronização de concorrência, respectivamente.

Criação da WaitGroup:

É criada uma WaitGroup chamada “wg” usando a declaração var wg sync.WaitGroup.

Loop para Criar Goroutines:

Um loop for é usado para criar três goroutines simulando tarefas independentes.

wg.Add(1) é chamado dentro do loop para adicionar 1 ao contador da WaitGroup para cada goroutine que será criada.

Isso aumenta o contador em 1 para cada goroutine, indicando que três goroutines precisam ser esperadas.

Em seguida, é iniciada uma nova goroutine anônima com go func(id int) { … }(i).

Cada goroutine recebe um argumento “id” que é igual ao valor atual de “i” no loop.

Goroutines:

Cada goroutine imprime uma mensagem indicando que está executando e usa defer wg.Done() para adiar a chamada a wg.Done().

Isso significa que wg.Done() será chamado quando a goroutine for concluída, o que decrementará o contador da WaitGroup em 1.

Aguardando Todas as Goroutines:

Após a criação das três goroutines, o programa principal chama wg.Wait().

Isso faz com que o programa principal aguarde até que o contador da WaitGroup seja zero.

O programa fica bloqueado até que todas as três goroutines tenham chamado wg.Done().

Mensagem de Conclusão:

Após todas as goroutines serem concluídas, ou seja, o contador da WaitGroup ser zero, o programa imprime “Todas as goroutines concluídas” usando fmt.Println.

Exemplo 2: Uso em um Pool de Trabalhadores

Um caso de uso comum para WaitGroups é em um pool de trabalhadores, onde várias goroutines trabalham em tarefas diferentes e o programa principal aguarda a conclusão de todas as tarefas.


package main

import (
    "fmt"
    "sync"
    "time"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Printf("Worker %d iniciado\n", id)
    time.Sleep(time.Second)
    fmt.Printf("Worker %d concluído\n", id)
}

func main() {
    var wg sync.WaitGroup
    numWorkers := 5

    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }

    wg.Wait()
    fmt.Println("Todas as tarefas concluídas")
}

Importações:

O programa importa as bibliotecas “fmt”, “sync” e “time”. “fmt” é usada para impressão de saída, “sync” para sincronização de concorrência e “time” para lidar com o tempo.

Função do Trabalhador (worker):

A função worker representa o trabalho que cada goroutine realizará.

defer wg.Done() é usado para garantir que wg.Done() seja chamado quando a goroutine for concluída.

Isso decrementa o contador da WaitGroup em 1.

fmt.Printf(“Worker %d iniciado\n”, id) imprime uma mensagem indicando que o trabalhador foi iniciado.

time.Sleep(time.Second) é usado para simular algum trabalho demorado (1 segundo de espera).

fmt.Printf(“Worker %d concluído\n”, id) imprime uma mensagem indicando que o trabalhador foi concluído.

Função Principal (main):

Uma WaitGroup chamada “wg” é criada para coordenar a sincronização das goroutines.

A variável “numWorkers” é definida para o número de goroutines que serão criadas (5 no caso).

Loop para Criar Goroutines:

Um loop for é usado para criar cinco goroutines, simulando cinco tarefas independentes.

wg.Add(1) é chamado dentro do loop para adicionar 1 ao contador da WaitGroup para cada goroutine que será criada.

Isso aumenta o contador em 1 para cada goroutine, indicando que cinco goroutines precisam ser esperadas.

Em seguida, é iniciada uma nova goroutine chamando go worker(i, &wg).

Cada goroutine recebe um argumento “i” que é igual ao valor atual do loop.

Aguardando Todas as Goroutines:

Após a criação das cinco goroutines, o programa principal chama wg.Wait().

Isso faz com que o programa principal aguarde até que o contador da WaitGroup seja zero.

O programa fica bloqueado até que todas as cinco goroutines tenham chamado wg.Done().

Conclusão:

Após todas as goroutines serem concluídas (ou seja, o contador da WaitGroup é zero), o programa imprime “Todas as tarefas concluídas” usando fmt.Println.

O time.Sleep(time.Second) no worker é usado para simular tarefas demoradas, e o defer wg.Done() garante que o contador da WaitGroup seja decrementado corretamente, mesmo se ocorrerem erros ou exceções nas goroutines.

Caso de Uso 3: Aguardando Várias Requisições HTTP

Você pode usar WaitGroups para aguardar a conclusão de várias requisições HTTP concorrentes.

Cada goroutine faz uma solicitação HTTP e, quando todas as solicitações forem concluídas, o programa principal pode processar os resultados.


package main

import (
	"fmt"
	"net/http"
	"sync"
)

func fetchURL(url string, wg *sync.WaitGroup) {
	defer wg.Done()
	resp, err := http.Get(url)
	if err != nil {
		fmt.Printf("Erro ao buscar URL %s: %v\n", url, err)
		return
	}
	defer resp.Body.Close()
	fmt.Printf("URL %s retornou com status: %s\n", url, resp.Status)
}

func main() {
	var wg sync.WaitGroup
	urls := []string{"https://example.com", "https://google.com", "https://github.com"}

	for _, url := range urls {
		wg.Add(1)
		go fetchURL(url, &wg)
	}

	wg.Wait()
	fmt.Println("Todas as requisições HTTP concluídas")
}

Importações:

O programa importa as bibliotecas “fmt” para impressão de saída e “net/http” para realizar solicitações HTTP.

Função fetchURL:

A função fetchURL é responsável por buscar uma URL usando uma solicitação HTTP GET.

defer wg.Done() é usado para garantir que wg.Done() seja chamado quando a goroutine for concluída, o que decrementa o contador da WaitGroup em 1.

http.Get(url) faz uma solicitação HTTP GET à URL fornecida.

Se houver um erro durante a solicitação, ele é tratado e um erro é impresso na saída.

defer resp.Body.Close() é usado para garantir que o corpo da resposta HTTP seja fechado após o uso.

Finalmente, o status da resposta HTTP é impresso.

Função Principal (main):

Uma WaitGroup chamada “wg” é criada para coordenar a sincronização das goroutines.

Uma slice de URLs é definida em “urls“, contendo três URLs para buscar.

Loop para Buscar URLs:

Um loop for é usado para percorrer cada URL em “urls“.

wg.Add(1) é chamado dentro do loop para adicionar 1 ao contador da WaitGroup para cada URL, indicando que três URLs precisam ser buscadas.

Em seguida, é iniciada uma nova goroutine chamando go fetchURL(url, &wg).

Cada goroutine busca uma URL específica.

Aguardando Todas as Goroutines:

Após a criação das três goroutines, o programa principal chama wg.Wait().

Isso faz com que o programa principal aguarde até que o contador da WaitGroup seja zero.

O programa fica bloqueado até que todas as três goroutines tenham chamado wg.Done().

Conclusão:

Após todas as goroutines serem concluídas, isto é, o contador da WaitGroup ser zero, o programa imprime “Todas as requisições HTTP concluídas” usando fmt.Println.

Isso indica que todas as solicitações HTTP foram concluídas.

Caso de Uso 4: Processamento de Dados em Lote

Você pode usar WaitGroups ao processar dados em lote, onde várias goroutines processam partes diferentes dos dados.

O programa principal pode aguardar até que todas as partes dos dados tenham sido processadas antes de prosseguir.


package main

import (
	"fmt"
	"sync"
)

func processDataBatch(batch []int, wg *sync.WaitGroup) {
	defer wg.Done()
	sum := 0
	for _, num := range batch {
		sum += num
	}
	fmt.Printf("Soma dos elementos no lote: %d\n", sum)
}

func main() {
	var wg sync.WaitGroup
	data := []int{1, 2, 3, 4, 5, 6, 7, 8}
	batchSize := 2

	for i := 0; i < len(data); i += batchSize { wg.Add(1) end := i + batchSize if end > len(data) {
			end = len(data)
		}
		go processDataBatch(data[i:end], &wg)
	}

	wg.Wait()
	fmt.Println("Processamento de dados em lote concluído")
}

Importações:

O código faz uso da biblioteca padrão do Go, sem importações adicionais.

Função processDataBatch:

func processDataBatch(batch []int, wg *sync.WaitGroup): Esta função é responsável por processar um lote (ou batch) de números inteiros.

defer wg.Done(): Usa defer para adiar a chamada de wg.Done(), o que decrementa o contador da WaitGroup (wg) quando a goroutine é concluída.

Calcula a soma dos elementos no lote e imprime o resultado usando fmt.Printf.

Função main:

func main(): A função principal do programa.

Cria uma variável wg do tipo sync.WaitGroup para coordenar a execução das goroutines.

Define uma slice chamada data que contém os dados a serem processados, no caso, números de 1 a 8.

batchSize define o tamanho do lote a ser processado de uma vez, neste caso, 2.

Loop para Processar em Lotes:

O loop for é usado para dividir os dados em lotes e processá-los em paralelo.

wg.Add(1) é chamado dentro do loop para adicionar 1 ao contador da WaitGroup (wg) para cada lote, indicando quantos lotes precisam ser processados.

O índice end é calculado para determinar onde termina o lote atual.

Se o próximo lote estiver além do tamanho dos dados, ele é ajustado para o tamanho dos dados.

Uma nova goroutine é iniciada chamando go processDataBatch(data[i:end], &wg).

Cada goroutine processa um lote específico dos dados.

Aguardando Todas as Goroutines:

Após a criação das goroutines para processar os lotes, o programa principal chama wg.Wait().

Isso faz com que o programa principal aguarde até que o contador da WaitGroup (wg) seja zero.

O programa fica bloqueado até que todas as goroutines tenham chamado wg.Done().

Conclusão:

Após todas as goroutines terem processado os lotes, ou seja, o contador da WaitGroup ser zero, o programa imprime “Processamento de dados em lote concluído” usando fmt.Println.

Isso indica que o processamento dos dados em lotes foi concluído.

Eu fico por aqui.

Até a próxima. 😉

Página principal do blog

Todas as aulas desse curso

Aula 36                        Aula 38

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>