ūüöÄ Aula 42 – Tutorial Golang – Stateful Goroutines

ūüöÄ Aula 42 – Tutorial Golang – Stateful Goroutines

Tutorial Golang

Tutorial Golang

P√°gina principal do blog

Todas as aulas desse curso

Aula 41                        Aula 43

Redes Sociais do Código Fluente:

facebook

 

 


Scarlett Finch

Scarlett Finch √© uma influenciadora, ūüé§ cantora e ūüé∂compositora pop brit√Ęnica , 24 anos de idade, adora o Brasil e em especial o Rio de Janeiro.

Ela foi criada utilizando algumas ferramentas de IA como a que vou apresentar hoje, o Foooocus.

Siga a Scarlett Finch no Instagram:

 

 


Conecte-se comigo!

LinkedIn: Fique à vontade para me adicionar no LinkedIn.

Ao conectar-se comigo, voc√™ ter√° acesso a atualiza√ß√Ķes regulares sobre desenvolvimento web, insights profissionais e oportunidades de networking no setor de tecnologia.

GitHub: Siga-me no GitHub para ficar por dentro dos meus projetos mais recentes, colaborar em código aberto ou simplesmente explorar os repositórios que eu contribuo, o que pode ajudar você a aprender mais sobre programação e desenvolvimento de software.

Recursos e Afiliados

Explorando os recursos abaixo, você ajuda a apoiar nosso site.

Somos parceiros afiliados das seguintes plataformas:

  • letsrecast.ai – Redefina a maneira como voc√™ consome artigos com Recast. Esta plataforma transforma artigos longos em di√°logos de √°udio que s√£o informativos, divertidos e f√°ceis de entender. Ideal para quem est√° sempre em movimento ou busca uma forma mais conveniente de se manter informado. Experimente Recast agora.
  • dupdub.com – Explore o universo do marketing digital com DupDub. Esta plataforma oferece ferramentas inovadoras e solu√ß√Ķes personalizadas para elevar a sua estrat√©gia de marketing online. Ideal para empresas que buscam aumentar sua visibilidade e efici√™ncia em campanhas digitais. Descubra mais sobre DupDub.
  • DeepBrain AI Studios – Revolucione a cria√ß√£o de conte√ļdo com a tecnologia de intelig√™ncia artificial da DeepBrain AI Studios. Esta plataforma avan√ßada permite que voc√™ crie v√≠deos interativos e apresenta√ß√Ķes utilizando avatares digitais gerados por IA, que podem simular conversas reais e intera√ß√Ķes humanas. Perfeito para educadores, criadores de conte√ļdo e empresas que querem inovar em suas comunica√ß√Ķes digitais. Explore DeepBrain AI Studios.
  • Audyo.ai – Transforme a maneira como voc√™ interage com conte√ļdo auditivo com Audyo.ai. Esta plataforma inovadora utiliza intelig√™ncia artificial para criar experi√™ncias de √°udio personalizadas, melhorando a acessibilidade e a compreens√£o de informa√ß√Ķes atrav√©s de podcasts, transcri√ß√Ķes autom√°ticas e s√≠ntese de voz avan√ßada. Ideal para profissionais de m√≠dia, educadores e qualquer pessoa que deseje acessar informa√ß√Ķes auditivas de maneira mais eficiente e envolvente. Descubra Audyo.ai e suas possibilidades.
  • Acoust.io – Transforme sua produ√ß√£o de √°udio com Acoust.io. Esta plataforma inovadora fornece uma suite completa de ferramentas para cria√ß√£o, edi√ß√£o e distribui√ß√£o de √°udio, ideal para artistas, produtores e empresas de m√≠dia em busca de excel√™ncia e inova√ß√£o sonora. Acoust.io simplifica o processo de levar suas ideias √† realidade, oferecendo solu√ß√Ķes de alta qualidade que elevam seus projetos de √°udio. Experimente Acoust.io agora e descubra um novo patamar de possibilidades para seu conte√ļdo sonoro.
  • Hostinger¬†– Hospedagem web acess√≠vel e confi√°vel. Ideal para quem busca solu√ß√Ķes de hospedagem de sites com excelente custo-benef√≠cio e suporte ao cliente robusto. Saiba mais sobre a Hostinger.
  • Digital Ocean¬†– Infraestrutura de nuvem para desenvolvedores. Oferece uma plataforma de nuvem confi√°vel e escal√°vel projetada especificamente para desenvolvedores que precisam de servidores virtuais, armazenamento e networking. Explore a Digital Ocean.
  • One.com – Solu√ß√Ķes simples e poderosas para o seu site. Uma escolha ideal para quem busca registrar dom√≠nios, hospedar sites ou criar presen√ßa online com facilidade e efici√™ncia. Visite One.com.

Educação e Networking

Amplie suas habilidades e sua rede participando de cursos gratuitos e comunidades de desenvolvedores:

Canais do Youtube

Explore nossos canais no YouTube para uma variedade de conte√ļdos educativos e de entretenimento, cada um com um foco √ļnico para enriquecer sua experi√™ncia de aprendizado e lazer.

Toti

Toti: Meu canal pessoal, onde posto clips artesanais de m√ļsicas que curto tocar, dicas de teoria musical, entre outras coisas.

Lofi Music Zone Beats

Lofi Music Zone Beats: O melhor da m√ļsica Lofi para estudo, trabalho e relaxamento, criando o ambiente perfeito para sua concentra√ß√£o.

Backing Track / Play-Along

Backing Track / Play-Along: Acompanhe faixas instrumentais para pr√°tica musical, ideal para m√ļsicos que desejam aprimorar suas habilidades.

Código Fluente

Código Fluente: Aulas gratuitas de programação, devops, IA, entre outras coisas.

Putz!

Putz!: Canal da banda Putz!, uma banda virtual, criada durante a pandemia com mais 3 amigos, F√°bio, Tat√° e Lula.

PIX para doa√ß√Ķes

PIX Nubank

PIX Nubank

ūüöÄ Aula 42 – Tutorial Golang – Stateful Goroutines

Código da aula: Github

Stateful Goroutines s√£o um poderoso padr√£o de design em Go para gerenciar o estado compartilhado em um ambiente concorrente.

Este padr√£o se alinha com a filosofia do Go de “n√£o comunicar compartilhando mem√≥ria,¬†em vez disso, compartilhar mem√≥ria comunicando-se“.

Não comunicar compartilhando memória

Este princípio desencoraja o padrão de design onde várias threads ou goroutines acessam diretamente e modificam um mesmo pedaço de memória (como variáveis globais ou estruturas de dados compartilhadas) para realizar comunicação entre si.

Esse acesso direto necessita de sincroniza√ß√£o complexa, geralmente usando mecanismos como mutexes (travas), para evitar problemas de concorr√™ncia, como condi√ß√Ķes de corrida, onde a ordem de execu√ß√£o n√£o determin√≠stica das opera√ß√Ķes pode levar a resultados inesperados ou erros.

Em vez disso, compartilhar memória comunicando-se

Go prop√Ķe uma abordagem diferente para a comunica√ß√£o entre goroutines: em vez de v√°rias goroutines acessarem diretamente o mesmo estado (compartilhamento de mem√≥ria), elas devem se comunicar enviando e recebendo dados explicitamente atrav√©s de canais.

Nesse modelo, cada pe√ßa de dados √© “propriedade” de uma √ļnica goroutine.

Se outra goroutine precisa desses dados ou deseja modificar esses dados, ela deve comunicar-se atrav√©s de canais, pedindo √† goroutine “propriet√°ria” dos dados que realize a opera√ß√£o.

Isso elimina a necessidade de sincronização explícita para acesso ao estado compartilhado, pois a comunicação através de canais já é segura por design.

Vejamos um Exemplo

Imagine um programa que mantém um contador.

Ao inv√©s de v√°rias goroutines incrementarem esse contador diretamente (compartilhamento de mem√≥ria) e precisarem sincronizar o acesso usando mutexes (o que seria “comunicar compartilhando mem√≥ria”), uma √ļnica goroutine poderia gerenciar o contador.

Outras goroutines enviariam mensagens (por exemplo, um valor +1) para essa goroutine gestora atrav√©s de um canal sempre que precisassem incrementar o contador (isso seria “compartilhar mem√≥ria comunicando-se”).

A goroutine gestora l√™ essas mensagens e atualiza o contador de forma segura, pois ela √© a √ļnica com acesso direto ao contador.

Essa abordagem simplifica o design do programa, tornando-o mais fácil de entender, manter e livre de erros comuns de programação concorrente.

Por Que e Quando Usar

  • Seguran√ßa de Concorr√™ncia: Quando m√ļltiplas goroutines precisam acessar ou modificar um estado compartilhado, o uso de Stateful Goroutines pode ajudar a evitar erros comuns relacionados √† concorr√™ncia, como condi√ß√Ķes de corrida.
  • Simplicidade e Clareza: Este padr√£o pode simplificar o design de sistemas concorrentes, tornando o c√≥digo mais f√°cil de entender e manter. A l√≥gica de manipula√ß√£o do estado √© centralizada, evitando a complexidade de sincronizar o acesso ao estado atrav√©s de mutexes ou outras primitivas de sincroniza√ß√£o.
  • Padr√Ķes de Comunica√ß√£o Flex√≠veis: Stateful Goroutines permitem que voc√™ modele uma variedade de padr√Ķes de comunica√ß√£o entre goroutines, facilitando a implementa√ß√£o de sistemas complexos, como pipelines de processamento, sistemas de mensagens e gerenciadores de recursos.

Quando N√£o Usar

  • Desempenho em Alto Volume: Embora Stateful Goroutines sejam eficientes para muitos casos de uso, eles podem se tornar um gargalo se o volume de solicita√ß√Ķes for extremamente alto, visto que o acesso ao estado √© serializado atrav√©s de uma √ļnica goroutine.
  • Sistemas Simples com Pouca Concorr√™ncia: Para sistemas simples ou quando a concorr√™ncia n√£o √© uma preocupa√ß√£o significativa, a introdu√ß√£o de Stateful Goroutines pode ser desnecess√°ria e levar a uma complexidade adicional sem benef√≠cios claros.
  • Necessidade de Acesso Concorrente de Alta Performance: Se o seu sistema requer acesso concorrente de alta performance ao estado compartilhado, estrat√©gias como o uso de v√°rias goroutines com segmenta√ß√£o do estado ou outras t√©cnicas avan√ßadas de concorr√™ncia podem ser mais apropriadas.

Exemplo 1: Sistema de Gerenciamento de Estado Concorrente

Este código exemplifica um sistema de gerenciamento de estado concorrente em Go, utilizando o conceito de Stateful Goroutines, isto é, Rotina Go com Estado.

O objetivo é gerenciar o acesso concorrente a um estado compartilhado (neste caso, um mapa) de forma segura, usando canais para sincronização.

Imagine uma central de atendimento onde apenas um atendente (a Stateful Goroutine) tem acesso a um arquivo de documentos (o estado compartilhado).

Cada pessoa que liga (outras goroutines) faz um pedido específico.

Algumas pedem para ler um documento (readOp), outras para adicionar ou alterar informa√ß√Ķes (writeOp) e algumas para remover um documento (deleteOp).

Cada pedido é feito por meio de um sistema de tickets (canais), garantindo que o atendente atenda a um pedido de cada vez.

Após o atendente processar o pedido, ele envia uma resposta pelo sistema, indicando que a ação foi concluída.

Conceito de Stateful Goroutines

Conceito de Stateful Goroutines


package main

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

type readOp struct {
	key  int
	resp chan int
}

type writeOp struct {
	key  int
	val  int
	resp chan bool
}

type deleteOp struct {
	key  int
	resp chan bool
}

type readAllOp struct {
	resp chan map[int]int
}

func main() {
	var readOps uint64
	var writeOps uint64
	var deleteOps uint64

	reads := make(chan readOp)
	writes := make(chan writeOp)
	deletes := make(chan deleteOp)
	readAlls := make(chan readAllOp)

	// Goroutine que gerencia o estado
	go func() {
		var state = make(map[int]int)
		for {
			select {
			case read := <-reads:
				read.resp <- state[read.key]
			case write := <-writes:
				state[write.key] = write.val
				write.resp <- true
			case delOp := <-deletes: // Renomeei 'delete' para 'delOp'
				_, exists := state[delOp.key]
				if exists {
					delete(state, delOp.key) // Uso correto da função delete
					delOp.resp <- true
				} else {
					delOp.resp <- false
				}
			case readAll := <-readAlls:
				stateCopy := make(map[int]int)
				for k, v := range state {
					stateCopy[k] = v
				}
				readAll.resp <- stateCopy
			}
		}
	}()

	// Simulando opera√ß√Ķes de leitura, escrita e dele√ß√£o
	for i := 0; i < 100; i++ {
		go func() {
			read := readOp{
				key:  rand.Intn(5),
				resp: make(chan int)}
			reads <- read
			<-read.resp
			atomic.AddUint64(&readOps, 1)
		}()
	}

	for i := 0; i < 10; i++ {
		go func() {
			write := writeOp{
				key:  rand.Intn(5),
				val:  rand.Intn(100),
				resp: make(chan bool)}
			writes <- write
			<-write.resp
			atomic.AddUint64(&writeOps, 1)
		}()
	}

	for i := 0; i < 5; i++ {
		go func() {
			delete := deleteOp{
				key:  rand.Intn(5),
				resp: make(chan bool)}
			deletes <- delete
			<-delete.resp
			atomic.AddUint64(&deleteOps, 1)
		}()
	}

	time.Sleep(time.Second)

	// Solicitar e imprimir o estado final
	readAll := readAllOp{resp: make(chan map[int]int)}
	readAlls <- readAll
	stateCopy := <-readAll.resp
	fmt.Println("Final state:", stateCopy)

	// Impress√£o dos contadores de opera√ß√Ķes
	fmt.Println("Read operations:", atomic.LoadUint64(&readOps))
	fmt.Println("Write operations:", atomic.LoadUint64(&writeOps))
	fmt.Println("Delete operations:", atomic.LoadUint64(&deleteOps))
}

Vamos detalhar o fluxo de execução:

Inicialização e Setup

  • Estruturas: O c√≥digo define estruturas para representar opera√ß√Ķes de leitura (readOp), escrita (writeOp), remo√ß√£o(deleteOp) e leitura total (readAllOp) sobre o estado compartilhado. Cada opera√ß√£o tem um canal resp para enviar a resposta.
  • Vari√°veis de Contagem: S√£o inicializadas vari√°veis para contar o n√ļmero de opera√ß√Ķes de leitura (readOps), escrita (writeOps) e dele√ß√£o (deleteOps) realizadas, usando tipos at√īmicos para garantir a seguran√ßa na concorr√™ncia.
  • Canais de Opera√ß√£o: S√£o criados canais para cada tipo de opera√ß√£o (reads, writes, deletes, readAll), que ser√£o usados para receber solicita√ß√Ķes dessas opera√ß√Ķes.

Goroutine de Gerenciamento de Estado

  • Uma goroutine √© iniciada para gerenciar o estado compartilhado, implementada em um loop infinito que aguarda opera√ß√Ķes solicitadas atrav√©s dos canais.
    • Leitura: Responde com o valor atual da chave solicitada.
    • Escrita: Atualiza o valor para a chave especificada.
    • Dele√ß√£o: Remove a chave especificada do estado, se existir.
    • Leitura Total: Envia uma c√≥pia do estado atual, evitando condi√ß√Ķes de corrida.

Simula√ß√£o de Opera√ß√Ķes

  • O c√≥digo simula opera√ß√Ķes concorrentes de leitura, escrita e dele√ß√£o ao criar m√ļltiplas goroutines que enviam solicita√ß√Ķes atrav√©s dos respectivos canais. Cada goroutine espera por uma resposta antes de incrementar o contador de opera√ß√Ķes, utilizando opera√ß√Ķes at√īmicas para garantir a correta contagem mesmo em um ambiente concorrente.

Espera e Resultados

  • O programa ent√£o pausa por um segundo (time.Sleep(time.Second)) para permitir que as goroutines concluam suas opera√ß√Ķes.
  • Ap√≥s a pausa, o c√≥digo solicita e imprime o estado final usando a opera√ß√£o de leitura total (readAllOp). Isso fornece uma vis√£o do estado ap√≥s as opera√ß√Ķes de leitura, escrita e dele√ß√£o.

Saída do Programa

  • Finalmente, o programa imprime os contadores de opera√ß√Ķes de leitura, escrita e dele√ß√£o, mostrando quantas opera√ß√Ķes de cada tipo foram realizadas.

Este código demonstra um padrão poderoso em Go para gerenciar estado compartilhado de forma segura em um ambiente de programação concorrente.

Ele utiliza Stateful Goroutines e canais para sincronizar o acesso ao estado, garantindo que as opera√ß√Ķes sejam realizadas de forma ordenada e segura, evitando problemas comuns de concorr√™ncia como condi√ß√Ķes de corrida.

Exemplo 2: Sistema de Filas de Mensagens

Outro uso comum para Stateful Goroutines é implementar um sistema de filas de mensagens, onde mensagens podem ser enviadas e consumidas por diferentes partes de um aplicativo.


package main

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

type message struct {
	content string
}

type sendMessage struct {
	msg  message
	resp chan bool
}

type getMessage struct {
	resp chan message
}

func messageQueue() (chan sendMessage, chan getMessage) {
	sendChan := make(chan sendMessage)
	getChan := make(chan getMessage)

	go func() {
		var queue []message
		for {
			if len(queue) > 0 {
				select {
				case msg := <-sendChan:
					queue = append(queue, msg.msg)
					msg.resp <- true
				case get := <-getChan:
					msg := queue[0]
					queue = queue[1:]
					get.resp <- msg
				}
			} else {
				msg := <-sendChan
				queue = append(queue, msg.msg)
				msg.resp <- true
			}
		}
	}()

	return sendChan, getChan
}

func main() {
	send, get := messageQueue()

	var wg sync.WaitGroup

	// Produtor de mensagens
	wg.Add(1)
	go func() {
		defer wg.Done()
		for i := 0; i < 5; i++ {
			respChan := make(chan bool) // create a response channel for each message
			send <- sendMessage{msg: message{content: fmt.Sprintf("Mensagem %d", i)}, resp: respChan}
			ok := <-respChan // wait for the send to be acknowledged
			if !ok {
				fmt.Println("Erro ao enviar mensagem")
				return
			}
			time.Sleep(1 * time.Second)
		}
	}()

	// Consumidor de mensagens
	wg.Add(1)
	go func() {
		defer wg.Done()
		for i := 0; i < 5; i++ {
			respChan := make(chan message) // create a response channel for each get request
			get <- getMessage{resp: respChan}
			msg := <-respChan // wait for the message to be received
			fmt.Println("Recebido:", msg.content)
		}
	}()

	wg.Wait() // wait for both producer and consumer to finish
}

Vamos detalhar o fluxo de execução:

1. Definição das Estruturas de Dados

  • message: Estrutura simples que cont√©m apenas um campo content do tipo string, usado para armazenar o conte√ļdo da mensagem.
  • sendMessage: Estrutura que inclui uma message e um canal de resposta resp do tipo chan bool. Este canal √© utilizado para sinalizar quando a mensagem foi processada (ou seja, adicionada √† fila).
  • getMessage: Estrutura que possui um canal de resposta resp do tipo chan message. Este canal √© utilizado para receber uma mensagem que foi retirada da fila.

2. Função messageQueue

Esta função inicializa e retorna dois canais: sendChan para enviar mensagens e getChan para receber mensagens. A função também inicia uma goroutine que gerencia a fila de mensagens:

  • Loop Infinito: A goroutine entra em um loop infinito, onde aguarda opera√ß√Ķes nos canais de envio e recebimento.
    • sendChan: Quando uma mensagem √© enviada atrav√©s deste canal, ela √© adicionada √† fila (queue), e uma confirma√ß√£o √© enviada de volta pelo canal resp da estrutura sendMessage.
    • getChan: Quando uma solicita√ß√£o √© feita atrav√©s deste canal, a primeira mensagem da fila √© removida e enviada de volta ao solicitante pelo canal resp da estrutura getMessage. Se a fila estiver vazia, a goroutine bloqueia neste ponto at√© que uma mensagem seja enviada.

3. Função main

A função principal do programa, onde a fila de mensagens é criada e são iniciadas as goroutines para produtores e consumidores de mensagens:

  • Inicializa√ß√£o de Goroutines:
    • Produtor de Mensagens: Uma goroutine √© criada para simular um produtor de mensagens. Ela envia um total de 5 mensagens, com intervalos de 1 segundo entre cada envio. Cada mensagem √© enviada atrav√©s do canal sendChan.
    • Consumidor de Mensagens: Outra goroutine √© iniciada para simular um consumidor de mensagens. Ela tenta receber um total de 5 mensagens atrav√©s do canal getChan. Cada mensagem recebida √© impressa na sa√≠da padr√£o.
  • Sincroniza√ß√£o com WaitGroup: Um sync.WaitGroup √© utilizado para garantir que o programa principal (main) aguarde a conclus√£o de ambas as goroutines antes de terminar. Isso evita que o programa termine prematuramente, o que interromperia as goroutines antes que elas completassem suas tarefas.

4. Conclus√£o do Processo

Ap√≥s as goroutines produtora e consumidora completarem suas execu√ß√Ķes, o WaitGroup sinaliza que o programa pode terminar, e a fun√ß√£o main conclui sua execu√ß√£o.

Este fluxo de execução mostra um exemplo prático e conciso de como goroutines e canais podem ser utilizados para implementar um sistema de filas de mensagens em Go, demonstrando a comunicação e sincronização eficazes entre diferentes partes de um programa.

Fico por aqui.

Continue aperfeiçoando suas habilidades e descobrindo as possibilidades ilimitadas que esta linguagem fascinante oferece.

Até a próxima!

P√°gina principal do blog

Todas as aulas desse curso

Aula 41                        Aula 43

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>