Producer-Consumer-Muster
Implementieren Sie mit Coroutinen eine Producer-Consumer-Pipeline
Producer-Consumer-Muster ist eine kostenlose Lua Academy-Lektion auf CoddyKit. Dies ist Lektion 4 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Lua Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Lua Academy-Kurs umfasst insgesamt 4 Lektionen.
Das Muster erklärt
Das Produzent-Konsument-Muster mit Coroutines: Der Produzent erzeugt Daten und gibt jedes Element über yield zurück; der Konsument empfängt die Elemente einzeln. Keiner muss die Implementierung des anderen kennen – die Kommunikation erfolgt über yield/resume. Dadurch werden Datenerzeugung und Datenverarbeitung voneinander entkoppelt.
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}))Unendlicher Produzent
Eine Produzenten-Coroutine kann eine unendliche Sequenz zurückgeben. Der Konsument bestimmt, wann angehalten wird. Das eignet sich hervorragend für Datenquellen mit Streaming: Messwerte von Sensoren, Log-Ereignisse oder generierte Sequenzen.
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 29Der Konsument als Coroutine
Auch der Konsument kann eine Coroutine sein, wodurch ein vollständig symmetrisches Produzent-Konsument-Paar entsteht. Eine dritte Funktion, der „Treiber“, koordiniert beide. Das ist nützlich für gepufferte Pipelines, in denen Produzent und Konsument mit unterschiedlichen Geschwindigkeiten arbeiten.
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)
endBackpressure
Backpressure bedeutet, dass der Konsument dem Produzenten signalisiert, langsamer zu werden. Bei Coroutines geschieht dies ganz natürlich: Der Produzent ruft nach jedem Element yield auf und wartet, bis der Konsument das nächste anfordert. Wenn der Konsument langsam ist, wartet der Produzent einfach.
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))Batch-Verarbeitung
Ein Produzent für Batch-Verarbeitung sammelt N Elemente, bevor er einen Batch zurückgibt. Der Konsument empfängt vollständige Batches und verarbeitet sie. Dadurch wird der Koordinationsaufwand bei Produzenten mit hoher Frequenz reduziert.
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: 10Produzent für Dateizeilen
Lesen Sie eine Datei verzögert: Eine Produzenten-Coroutine liest die Zeilen einzeln und gibt jede über yield zurück. Der Konsument verarbeitet die Zeilen, ohne die gesamte Datei in den Speicher laden zu müssen.
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)Transformationsstufe
Fügen Sie zwischen Produzent und Konsument eine Transformationsstufe ein. Die Transformation liest aus einer Coroutine und gibt verarbeitete Werte an eine andere zurück. Erstellen Sie Daten-Pipelines, indem Sie solche Stufen verketten.
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 12Fehlerweitergabe in Pipelines
Fehler in Coroutine-Pipelines werden über den Zustand dead oder über pcall weitergegeben. Umschließen Sie Pipeline-Stufen mit pcall, um Fehler abzufangen und angemessen zu behandeln, ohne die gesamte Pipeline zum Absturz zu bringen.
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) endProduzenten zusammenführen
Fassen Sie mehrere Produzenten zu einem zusammen: Wechseln Sie im Round-Robin-Verfahren zwischen allen aktiven Produzenten und geben Sie nacheinander jeweils ein Element zurück. Beenden Sie den Vorgang, wenn alle Produzenten erschöpft sind.
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 6Praxisbeispiel
Pipeline zur Log-Verarbeitung: Der Produzent liest Log-Zeilen, die Filterstufe behält nur Fehlerzeilen, die Transformationsstufe extrahiert wichtige Informationen und der Konsument gruppiert nach Fehlertyp. Jede Stufe ist eine unabhängige und testbare Coroutine.
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 failedSchnelltest
Wie verarbeitet das Produzent-Konsument-Muster mit Coroutines Backpressure?
Rückblick: Produzent und Konsument
Zusammenfassung:
- Der Produzent gibt Elemente über yield zurück; der Konsument steuert ihn durch Aufrufe von resume
- Unendliche Produzenten:
while true do yield() end - Pipelines: Verketten Sie Transformations-Coroutines zwischen Produzent und Konsument
- Backpressure erfolgt automatisch: Der Produzent wartet auf den Konsumenten
- Batch-, Filter- und Zusammenführungsstufen als Coroutine-Wrapper
- Verwenden Sie pcall in Pipeline-Stufen zur Isolierung von Fehlern
Häufig gestellte Fragen
Ist die Lektion „Producer-Consumer-Muster“ kostenlos?
Ja — der vollständige Text von „Producer-Consumer-Muster“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Lua Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Lua Academy-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „Producer-Consumer-Muster“?
Implementieren Sie mit Coroutinen eine Producer-Consumer-Pipeline Du übst Lua Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um Lua Academy zu starten?
Keine Vorkenntnisse erforderlich. Lua Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 4 von 4.
Wie lange dauert die Lektion „Producer-Consumer-Muster“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser Lua Academy-Lektion Code schreiben und ausführen?
Ja. Jede Lua Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- Coroutinen erstellen
- resume und yield
- Coroutine-Status
- Producer-Consumer-Muster