DEV Community

Celso Costa
Celso Costa

Posted on

Producer/Consumer (Produtor/Consumidor)

Definição

Consideramos dois processos, chamados de “produtor” e “consumidor”, respectivamente. O produtor é um processo cíclico e cada vez que passa pelo seu ciclo produz uma determinada porção de informação, que deve ser processada pelo consumidor. O consumidor também é um processo cíclico e cada vez que passa pelo seu ciclo pode processar a próxima porção de informação, tal como foi produzida pelo produtor. Um exemplo simples é dado por um processo computacional, que produz como “porções de informação” imagens de cartões perfurados a serem perfurados por um cartão perfurado, que desempenha o papel do consumidor.[1]

Image description

Explicação

Um produtor cria itens e os armazena em uma estrutura de dados, enquanto um consumidor remove os itens dessa estrutura e os processa.

Se o consumo for maior que a produção o buffer(estrutura de dados) esvazia, e o consumidor não tem o que consumir
Se o consumo for menor que a produção o buffer enche, e o produtor não consegue adicionar mais itens. Esse é um problema clássico chamado de buffer limitado.

Contextualização do Problema

Suponha que temos um produtor que publica um e-mail no buffer, e um consumidor que consome o e-mail do buffer e exibe uma mensagem informando que foi enviado um e-mail com a nova senha de acesso para o e-mail informado.

Implementação em go

 package main import ( "fmt" "os" "strconv" "sync" "time" ) type buffer struct { items []string mu sync.Mutex } func (buff *buffer) add(item string) { buff.mu.Lock() defer buff.mu.Unlock() if len(buff.items) < 5 { buff.items = append(buff.items, item) // fmt.Println("Foi adicionado o item " + item) } else { fmt.Println("O Buffer não pode armazenar nenhum item mais está com a capacidade máxima") os.Exit(0) } } func (buff *buffer) get() string { buff.mu.Lock() defer buff.mu.Unlock() if len(buff.items) == 0 { return "" } target := buff.items[0] buff.items = buff.items[1:] return target } var wg sync.WaitGroup func main() { buff := buffer{} wg.Add(2) go producer(&buff) go consumer(&buff) wg.Wait() } func producer(buff *buffer) { defer wg.Done() for index := 1; ; index++ { str := strconv.Itoa(index) + "@email.com" buff.add(str) time.Sleep(5 * time.Millisecond) // Adiciona um pequeno atraso para simular produção } } func consumer(buff *buffer) { defer wg.Done() for { data := buff.get() if data != "" { fmt.Println("Enviado um email com a nova senha de acesso para: " + data) } } } 
Enter fullscreen mode Exit fullscreen mode

Explicando a implementação

  • Primeiro, criamos uma estrutura chamada buffer, que contém um array de strings chamado items e um mecanismo de controle do tipo mutex, chamado mu, para gerenciar o acesso concorrente.
  • Temos duas funções: uma chamada add, que basicamente adiciona um item ao buffer, desde que haja espaço disponível, já que a capacidade do buffer é de apenas 5 itens; e outra chamada get, que, se houver itens no buffer, retorna o primeiro elemento e remove esse elemento do buffer.
  • O Producer basicamente pega o index do loop e o concatena em uma string chamada str, que contém o índice e um domínio de e-mail fictício, e adiciona no buffer. Foi adicionado um intervalo de tempo para simular um atraso.
  • O Consumer solicita ao buffer um item, caso tenha ao menos um item. Em seguida, o Consumer exibe uma mensagem na tela informando que foi enviado um e-mail com a nova senha de acesso para o item que foi publicado publicado no buffer.

Link do código: https://github.com/jcelsocosta/race_condition/blob/main/producerconsumer/buffer/producerconsumer.go

Referência

  1. https://www.cs.utexas.edu/~EWD/transcriptions/EWD01xx/EWD123.html#4.1.%20Typical%20Uses%20of%20the%20General%20Semaphore.

Bibliografia

https://www.cin.ufpe.br/~cagf/if677/2015-2/slides/08_Concorrencia%20(Jorge).pdf

Top comments (1)

Collapse
 
tired_student profile image
Vinícius Sales Oliveira

Ótima explicação!