0Pricing
Lua Academy · درس

نمط المنتج والمستهلك

نفّذ خط أنابيب المنتج والمستهلك باستخدام البرامج التعاونية

نمط المنتج والمستهلك درس مجاني في Lua Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Lua Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Lua Academy 4 دروس في المجموع.

شرح النمط

نمط المنتج والمستهلك باستخدام الكوروتينات: ينشئ المنتج البيانات وينفذ yield لكل عنصر؛ ويتلقى المستهلك العناصر واحدًا تلو الآخر. ولا يحتاج أي منهما إلى معرفة كيفية تنفيذ الآخر؛ إذ يتواصلان عبر yield/resume. وبهذا ينفصل توليد البيانات عن معالجتها.

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

منتج لا نهائي

يمكن لكوروتين المنتج إنشاء تسلسل لا نهائي باستخدام yield. ويتحكم المستهلك في وقت التوقف. وهذا مناسب تمامًا لمصادر البيانات المتدفقة، مثل قراءات المستشعرات وأحداث السجل والتسلسلات المُولّدة.

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

المستهلك بوصفه كوروتينًا

يمكن أن يكون المستهلك كوروتينًا أيضًا، مما ينشئ زوجًا متماثلًا بالكامل من المنتج والمستهلك. وتتولى دالة ثالثة تسمى «المشغّل» تنسيق عملهما. ويفيد ذلك في خطوط الأنابيب ذات التخزين المؤقت، حيث يختلف معدلا المنتج والمستهلك.

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

الضغط العكسي

يحدث الضغط العكسي عندما يشير المستهلك إلى المنتج بضرورة الإبطاء. وباستخدام الكوروتينات، يحدث ذلك بصورة طبيعية: ينفذ المنتج yield بعد كل عنصر، وينتظر أن يطلب المستهلك العنصر التالي. وإذا كان المستهلك بطيئًا، ينتظر المنتج ببساطة.

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

المعالجة على دفعات

يجمع المنتج الذي يستخدم الدفعات عددًا قدره N من العناصر قبل تنفيذ yield لإرجاع دفعة. ويتلقى المستهلك الدفعات الكاملة ويعالجها. ويقلل ذلك من تكلفة التنسيق للمنتجات عالية التردد.

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

منتج أسطر الملف

اقرأ ملفًا بطريقة كسولة: يقرأ كوروتين المنتج الأسطر واحدًا تلو الآخر وينفذ yield لكل سطر. ويعالج المستهلك الأسطر من دون تحميل الملف بأكمله إلى الذاكرة.

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)

مرحلة التحويل

أدرج مرحلة تحويل بين المنتج والمستهلك. تقرأ مرحلة التحويل من كوروتين واحد وتنفذ yield للقيم المعالجة إلى كوروتين آخر. وابنِ خطوط أنابيب للبيانات عبر ربط هذه المراحل.

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

انتشار الأخطاء في خطوط الأنابيب

تنتشر الأخطاء في خطوط أنابيب الكوروتينات عبر الحالة المنتهية أو باستخدام pcall. غلّف مراحل خط الأنابيب داخل pcall لالتقاط الأخطاء ومعالجتها بسلاسة من دون تعطيل خط الأنابيب بأكمله.

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

دمج المنتجات

ادمج عدة منتجات في منتج واحد: تناوب بينها جميعًا ما دامت حية، وأعد كل عنصر بالتتابع. وتوقف عندما تنفد جميع المنتجات.

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

حالة استخدام واقعية

خط أنابيب لمعالجة السجلات: يقرأ المنتج أسطر السجل، وتحتفظ مرحلة التصفية بأسطر الأخطاء فقط، وتستخرج مرحلة التحويل المعلومات الأساسية، ثم يجمع المستهلك البيانات حسب نوع الخطأ. وكل مرحلة كوروتين مستقل قابل للاختبار.

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

اختبار سريع

كيف يتعامل نمط المنتج والمستهلك باستخدام الكوروتينات مع الضغط العكسي؟

مراجعة: المنتج والمستهلك

الملخص:

  • ينفذ المنتج yield للعناصر؛ ويقود المستهلك العملية باستدعاء resume
  • المنتجات اللانهائية: while true do yield() end
  • خطوط الأنابيب: اربط كوروتينات التحويل بين المنتج والمستهلك
  • الضغط العكسي تلقائي: ينتظر المنتج المستهلك
  • مراحل الدفعات والتصفية والدمج بوصفها أغلفة للكوروتينات
  • استخدم pcall في مراحل خط الأنابيب لعزل الأخطاء

الأسئلة الشائعة

هل درس «نمط المنتج والمستهلك» مجاني؟

نعم — نص درس «نمط المنتج والمستهلك» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Lua Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Lua Academy 4 دروس في المجموع.

ماذا ستتعلم في «نمط المنتج والمستهلك»؟

نفّذ خط أنابيب المنتج والمستهلك باستخدام البرامج التعاونية تتمرن على Lua Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Lua Academy؟

لا تُشترط خبرة سابقة. Lua Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «نمط المنتج والمستهلك»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Lua Academy هذا؟

نعم. كل درس في Lua Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. إنشاء البرامج التعاونية
  2. ‏resume وyield
  3. حالة البرنامج التعاوني
  4. نمط المنتج والمستهلك
← العودة إلى Lua Academy