Modèle producteur-consommateur
Implémentez un pipeline producteur-consommateur à l’aide de coroutines.
Modèle producteur-consommateur est une leçon Lua Academy gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Lua Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Lua Academy comprend 4 leçons au total.
Explication du schéma
Le schéma producteur-consommateur avec des coroutines : le producteur génère les données et utilise yield pour fournir chaque élément ; le consommateur reçoit les éléments un par un. Aucun des deux n’a besoin de connaître l’implémentation de l’autre : ils communiquent via yield/resume. Cela découple la génération des données de leur traitement.
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}))Producteur infini
Une coroutine productrice peut fournir une séquence infinie avec yield. Le consommateur contrôle le moment où il s’arrête. C’est idéal pour les sources de données en flux continu : relevés de capteurs, événements de journal et séquences générées.
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 29Le consommateur comme coroutine
Le consommateur peut également être une coroutine, ce qui crée une paire producteur-consommateur entièrement symétrique. Une troisième fonction, appelée « pilote », orchestre les deux. Cette approche est utile pour les chaînes de traitement avec tampon, lorsque le producteur et le consommateur avancent à des rythmes différents.
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)
endContre-pression
La contre-pression se produit lorsque le consommateur demande au producteur de ralentir. Avec les coroutines, cela se fait naturellement : le producteur utilise yield après chaque élément et attend que le consommateur demande le suivant. Si le consommateur est lent, le producteur attend simplement.
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))Traitement par lots
Un producteur par lots collecte N éléments avant de fournir un lot avec yield. Le consommateur reçoit et traite des lots complets. Cela réduit le coût de coordination pour les producteurs à haute fréquence.
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: 10Producteur de lignes de fichier
Lisez un fichier de manière paresseuse : une coroutine productrice lit les lignes une par une et fournit chacune d’elles avec yield. Le consommateur traite les lignes sans charger tout le fichier en mémoire.
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)Étape de transformation
Insérez une étape de transformation entre le producteur et le consommateur. La transformation lit les données d’une coroutine et fournit les valeurs traitées à une autre avec yield. Créez des chaînes de traitement de données en reliant ces étapes.
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 12Propagation des erreurs dans les chaînes de traitement
Dans les chaînes de traitement fondées sur des coroutines, les erreurs se propagent via l’état mort ou via pcall. Encadrez les étapes de la chaîne avec pcall pour intercepter et gérer les erreurs correctement sans faire planter toute la chaîne.
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) endFusionner des producteurs
Combinez plusieurs producteurs en un seul : répartissez l’exécution à tour de rôle entre tous les producteurs encore actifs et fournissez chaque élément successivement. Arrêtez-vous lorsque tous les producteurs sont épuisés.
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 6Cas d’utilisation concret
Chaîne de traitement de journaux : le producteur lit les lignes du journal, l’étape de filtrage ne conserve que les lignes d’erreur, l’étape de transformation extrait les informations clés et le consommateur regroupe les résultats par type d’erreur. Chaque étape est une coroutine indépendante et testable.
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 failedVérification rapide
Comment le schéma de coroutine producteur-consommateur gère-t-il la contre-pression ?
Récapitulatif : producteur-consommateur
Résumé :
- Le producteur fournit les éléments avec yield ; le consommateur le pilote en appelant resume
- Producteurs infinis :
while true do yield() end - Chaînes de traitement : reliez des coroutines de transformation entre le producteur et le consommateur
- La contre-pression est automatique : le producteur attend le consommateur
- Utilisez des étapes d’assemblage, de filtrage et de fusion comme enveloppes de coroutines
- Utilisez pcall dans les étapes de la chaîne pour isoler les erreurs
Questions Fréquemment Posées
La leçon « Modèle producteur-consommateur » est-elle gratuite ?
Oui — le texte complet de « Modèle producteur-consommateur » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Lua Academy, passe à CoddyKit PRO. Le cours Lua Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Modèle producteur-consommateur » ?
Implémentez un pipeline producteur-consommateur à l’aide de coroutines. Tu pratiques Lua Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer Lua Academy ?
Aucune expérience préalable n'est requise. Lua Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.
Combien de temps prend la leçon « Modèle producteur-consommateur » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon Lua Academy ?
Oui. Chaque leçon Lua Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Créer des coroutines
- resume et yield
- État d’une coroutine
- Modèle producteur-consommateur