0Pricing
Lua Academy · Leçon

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 29

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

Contre-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: 10

Producteur 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 12

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

Fusionner 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 6

Cas 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 failed

Vé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

  1. Créer des coroutines
  2. resume et yield
  3. État d’une coroutine
  4. Modèle producteur-consommateur
← Retour à Lua Academy