0Pricing
Lua Academy · Lezione

Pattern produttore-consumatore

Implementi una pipeline produttore-consumatore usando le coroutine.

Pattern produttore-consumatore è una lezione Lua Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Lua Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Lua Academy include 4 lezioni in totale.

Spiegazione del modello

Il modello produttore-consumatore con le coroutine: il produttore genera i dati e restituisce ogni elemento tramite yield; il consumatore riceve gli elementi uno alla volta. Nessuno dei due deve conoscere l'implementazione dell'altro: comunicano tramite yield/resume. In questo modo la generazione dei dati è separata dalla loro elaborazione.

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}))

Produttore infinito

Una coroutine produttrice può restituire una sequenza infinita tramite yield. È il consumatore a controllare quando fermarsi. Questo è ideale per le sorgenti di dati in streaming: letture di sensori, eventi di log e sequenze generate.

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

Il consumatore come coroutine

Anche il consumatore può essere una coroutine, creando una coppia produttore-consumatore completamente simmetrica. Una terza funzione, detta «driver», coordina entrambe. Questo è utile per le pipeline con buffer, quando produttore e consumatore hanno velocità diverse.

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

Controllo del flusso a ritroso

Il controllo del flusso a ritroso si verifica quando il consumatore segnala al produttore di rallentare. Con le coroutine, questo avviene naturalmente: il produttore esegue yield dopo ogni elemento e attende che il consumatore richieda quello successivo. Se il consumatore è lento, il produttore semplicemente attende.

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))

Elaborazione a batch

Un produttore che lavora a batch raccoglie N elementi prima di restituire un batch tramite yield. Il consumatore riceve ed elabora batch completi. Questo riduce il sovraccarico di coordinamento per i produttori ad alta frequenza.

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

Produttore di righe di file

Legga un file in modo lazy: una coroutine produttrice legge le righe una alla volta e restituisce ciascuna riga tramite yield. Il consumatore elabora le righe senza caricare l'intero file in memoria.

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)

Fase di trasformazione

Inserisca una fase di trasformazione tra produttore e consumatore. La trasformazione legge da una coroutine e restituisce tramite yield i valori elaborati a un'altra. Crei pipeline di dati concatenando queste fasi.

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

Propagazione degli errori nelle pipeline

Gli errori nelle pipeline di coroutine si propagano tramite lo stato terminato o tramite pcall. Avvolga le fasi della pipeline in pcall per intercettare e gestire gli errori in modo corretto, senza arrestare l'intera pipeline.

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

Unione dei produttori

Combini più produttori in uno solo: alterni i produttori ancora attivi in modalità round-robin, restituendo ogni volta l'elemento successivo. Si fermi quando tutti i produttori hanno esaurito gli elementi.

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 d'uso reale

Pipeline per l'elaborazione dei log: il produttore legge le righe del log, la fase di filtro mantiene solo le righe relative agli errori, la fase di trasformazione estrae le informazioni principali e il consumatore aggrega i dati per tipo di errore. Ogni fase è una coroutine indipendente e verificabile.

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 rapida

In che modo il modello di coroutine produttore-consumatore gestisce il controllo del flusso a ritroso?

Riepilogo: produttore-consumatore

Riepilogo:

  • Il produttore restituisce gli elementi tramite yield; il consumatore lo controlla chiamando resume
  • Produttori infiniti: while true do yield() end
  • Pipeline: concateni coroutine di trasformazione tra produttore e consumatore
  • Il controllo del flusso a ritroso è automatico: il produttore attende il consumatore
  • Batch, filtri e unioni come wrapper di coroutine
  • Utilizzi pcall nelle fasi della pipeline per isolare gli errori

Domande Frequenti

La lezione «Pattern produttore-consumatore» è gratuita?

Sì — il testo completo di «Pattern produttore-consumatore» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Lua Academy, passa a CoddyKit PRO. Il corso Lua Academy include 4 lezioni in totale.

Cosa imparerò in «Pattern produttore-consumatore»?

Implementi una pipeline produttore-consumatore usando le coroutine. Eserciti Lua Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Lua Academy?

Non è richiesta alcuna esperienza precedente. Lua Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.

Quanto tempo richiede la lezione «Pattern produttore-consumatore»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Lua Academy?

Sì. Ogni lezione Lua Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Creazione delle coroutine
  2. resume e yield
  3. Stato delle coroutine
  4. Pattern produttore-consumatore
← Torna a Lua Academy