0Pricing
Lua Academy · Aula

Padrão produtor-consumidor

Implemente um fluxo produtor-consumidor usando corrotinas.

Padrão produtor-consumidor é uma aula grátis de Lua Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Lua Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Lua Academy inclui 4 aulas no total.

Explicação do padrão

O padrão produtor-consumidor com corrotinas: o produtor gera dados e fornece cada item por meio de yield; o consumidor recebe os itens um de cada vez. Nenhum dos dois precisa conhecer a implementação do outro — eles se comunicam por meio de yield/resume. Isso desacopla a geração do processamento de dados.

local function makeProducer(items)
  return coroutine.create(function()
    for _, item in ipairs(items) do
      coroutine.yield(item)
    end
  end)
end

local function consume(producer)
  while true do
    local ok, item = coroutine.resume(producer)
    if not ok or item == nil then break end
    print("Processing:", item)
  end
end

consume(makeProducer({10, 20, 30, 40}))

Produtor infinito

Uma corrotina produtora pode fornecer uma sequência infinita. O consumidor controla quando parar. Isso é perfeito para fontes de dados contínuos: leituras de sensores, eventos de registro e sequências geradas.

local function primes()
  return coroutine.wrap(function()
    local function isPrime(n)
      if n<2 then return false end
      for i=2, math.floor(math.sqrt(n)) do
        if n%i==0 then return false end
      end
      return true
    end
    local n = 2
    while true do
      if isPrime(n) then coroutine.yield(n) end
      n = n + 1
    end
  end)
end

local gen = primes()
for i = 1, 10 do
  io.write(gen() .. " ")
end
print()  -- 2 3 5 7 11 13 17 19 23 29

Consumidor como corrotina

O consumidor também pode ser uma corrotina, criando um par produtor-consumidor totalmente simétrico. Uma terceira função, o “controlador”, coordena ambos. Isso é útil para cadeias de processamento com buffer, nas quais o produtor e o consumidor têm ritmos diferentes.

local function producer()
  return coroutine.create(function(consumer)
    for i = 1, 5 do
      coroutine.resume(consumer, i * 10)
      coroutine.yield()  -- wait
    end
  end)
end

local consumer = coroutine.create(function()
  while true do
    local val = coroutine.yield()
    print("Received:", val)
  end
end)

coroutine.resume(consumer)  -- start, waits for data
local pro = producer()
for i = 1, 5 do
  coroutine.resume(pro, consumer)
end

Contrapressão

Contrapressão ocorre quando o consumidor sinaliza ao produtor para diminuir o ritmo. Com corrotinas, isso acontece naturalmente: o produtor produz um yield após cada item e aguarda o consumidor solicitar o próximo. Se o consumidor for lento, o produtor simplesmente espera.

local function slowConsumer(gen)
  for v in gen do
    -- Simulate slow processing
    print("Processing", v, "...")
    -- In real code: os.execute("sleep 0.1")
  end
end

local function fastProducer(n)
  return coroutine.wrap(function()
    for i = 1, n do
      print("Producing", i)
      coroutine.yield(i)
    end
  end)
end

-- Producer automatically waits for consumer:
slowConsumer(fastProducer(4))

Processamento em lotes

Um produtor em lotes coleta N itens antes de produzir um lote. O consumidor recebe e processa lotes completos. Isso reduz o custo de coordenação para produtores de alta frequência.

local function batchProducer(items, batchSize)
  return coroutine.wrap(function()
    local batch = {}
    for _, item in ipairs(items) do
      batch[#batch+1] = item
      if #batch >= batchSize then
        coroutine.yield(batch)
        batch = {}
      end
    end
    if #batch > 0 then coroutine.yield(batch) end
  end)
end

local data = {1,2,3,4,5,6,7,8,9,10}
for batch in batchProducer(data, 3) do
  print("Batch:", table.concat(batch, ","))
end
-- Batch: 1,2,3
-- Batch: 4,5,6
-- Batch: 7,8,9
-- Batch: 10

Produtor de linhas de arquivo

Leia um arquivo de forma preguiçosa: uma corrotina produtora lê as linhas uma de cada vez e produz cada uma. O consumidor processa as linhas sem carregar o arquivo inteiro na memória.

local function lineProducer(path)
  return coroutine.wrap(function()
    local f = io.open(path, "r")
    if not f then return end
    for line in f:lines() do
      coroutine.yield(line)
    end
    f:close()
  end)
end

-- Process file line by line without loading all at once
local wordCount = 0
for line in lineProducer("data.txt") do
  for _ in line:gmatch("%S+") do
    wordCount = wordCount + 1
  end
end
print("Words:", wordCount)

Etapa de transformação

Insira uma etapa de transformação entre o produtor e o consumidor. A transformação lê de uma corrotina e fornece valores processados a outra. Crie cadeias de processamento de dados encadeando essas etapas.

local function transform(source, fn)
  return coroutine.wrap(function()
    for v in source do
      coroutine.yield(fn(v))
    end
  end)
end

local numbers = coroutine.wrap(function()
  for i = 1, 6 do coroutine.yield(i) end
end)

local doubled = transform(numbers, function(n) return n*2 end)
local filtered = coroutine.wrap(function()
  for v in doubled do
    if v > 4 then coroutine.yield(v) end
  end
end)

for v in filtered do io.write(v.." ") end
print()  -- 6 8 10 12

Propagação de erros em cadeias de processamento

Os erros em cadeias de processamento de corrotinas propagam-se pelo estado morto ou por meio de pcall. Envolva as etapas da cadeia em pcall para capturar e tratar erros adequadamente, sem interromper toda a cadeia.

local function safeStage(source, fn)
  return coroutine.wrap(function()
    for v in source do
      local ok, result = pcall(fn, v)
      if ok then
        coroutine.yield(result)
      else
        print("Error processing", v, ":", result)
      end
    end
  end)
end

local data = coroutine.wrap(function()
  for _, v in ipairs({4, -1, 9, 0, 16}) do coroutine.yield(v) end
end)

local results = safeStage(data, function(n)
  assert(n > 0, "non-positive")
  return math.sqrt(n)
end)

for v in results do print(v) end

Mesclando produtores

Combine vários produtores em um só: alterne entre todos os produtores ativos, fornecendo cada item por vez. Pare quando todos os produtores estiverem esgotados.

local function merge(...)
  local producers = {...}
  return coroutine.wrap(function()
    while #producers > 0 do
      local alive = {}
      for _, co in ipairs(producers) do
        local ok, v = coroutine.resume(co)
        if ok and v ~= nil then
          coroutine.yield(v)
          alive[#alive+1] = co
        end
      end
      producers = alive
    end
  end)
end

local function src(items)
  return coroutine.create(function()
    for _,v in ipairs(items) do coroutine.yield(v) end
  end)
end

for v in merge(src({1,3,5}),src({2,4,6})) do
  io.write(v.." ")
end
print()  -- 1 2 3 4 5 6

Caso de uso no mundo real

Cadeia de processamento de registros: o produtor lê linhas de registro, a etapa de filtragem mantém apenas as linhas de erro, a etapa de transformação extrai informações importantes e o consumidor agrupa por tipo de erro. Cada etapa é uma corrotina independente e testável.

local function makeFilter(src, pred)
  return coroutine.wrap(function()
    for line in src do
      if pred(line) then coroutine.yield(line) end
    end
  end)
end

local logs = coroutine.wrap(function()
  local entries = {
    "INFO: started",
    "ERROR: timeout",
    "DEBUG: connecting",
    "ERROR: auth failed",
  }
  for _, e in ipairs(entries) do coroutine.yield(e) end
end)

local errors = makeFilter(logs, function(l) return l:match("^ERROR") end)
for line in errors do print(line) end
-- ERROR: timeout
-- ERROR: auth failed

Verificação rápida

Como o padrão de corrotina produtor-consumidor lida com a contrapressão?

Recapitulação: produtor-consumidor

Resumo:

  • O produtor fornece itens; o consumidor conduz chamando resume
  • Produtores infinitos: while true do yield() end
  • Cadeias de processamento: encadeie corrotinas de transformação entre o produtor e o consumidor
  • A contrapressão é automática: o produtor aguarda o consumidor
  • Use etapas de lote, filtragem e mesclagem como encapsuladores de corrotinas
  • Use pcall nas etapas da cadeia para isolar erros

Perguntas Frequentes

A aula “Padrão produtor-consumidor” é grátis?

Sim — o texto completo de “Padrão produtor-consumidor” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Lua Academy, atualize para CoddyKit PRO. O curso de Lua Academy inclui 4 aulas no total.

O que vou aprender em “Padrão produtor-consumidor”?

Implemente um fluxo produtor-consumidor usando corrotinas. Você pratica Lua Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Lua Academy?

Nenhuma experiência prévia é necessária. Lua Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.

Quanto tempo leva a aula “Padrão produtor-consumidor”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Lua Academy?

Sim. Cada aula de Lua Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Criando corrotinas
  2. resume e yield
  3. Estado da corrotina
  4. Padrão produtor-consumidor
← Voltar para Lua Academy