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 29Il 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)
endControllo 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: 10Produttore 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 12Propagazione 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) endUnione 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 6Caso 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 failedVerifica 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
- Creazione delle coroutine
- resume e yield
- Stato delle coroutine
- Pattern produttore-consumatore