لوا المتوازي

*This content is translated using AI (Beta) and may contain errors. To view this page in English, click here.

مع نموذج برمجة لوا المتوازي، يمكنك تشغيل التعليمات البرمجية على عدة خيوط في وقت واحد، مما يمكن أن يحسن أداء لعبتك. مع توسيع لعبتك بمزيد من المحتوى، يمكنك اعتماد هذا النموذج للمساعدة في الحفاظ على الأداء وسلامة نصوص لوا الخاصة بك.

نموذج البرمجة المتوازية

بشكل افتراضي، يتم تنفيذ النصوص بشكل تسلسلي. إذا كانت لعبتك تحتوي على منطق أو محتوى معقد، مثل الشخصيات غير القابلة للعب (NPCs)، والتحقق من صحة الأشعة، والتوليد الإجرائي، فقد يتسبب التنفيذ التسلسلي في تأخير لمستخدميك. مع نموذج البرمجة المتوازية، يمكنك تقسيم المهام إلى نصوص متعددة وتشغيلها بشكل متوازي. هذا يجعل كود لعبتك يعمل بشكل أسرع، مما يحسن تجربة المستخدم.

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

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

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

تقسيم الكود إلى خيوط متعددة

لتشغيل نصوص لعبتك في خيوط متعددة بشكل متزامن، تحتاج إلى تقسيمها إلى أجزاء منطقية تحت عوامل مختلفة في نموذج البيانات. يتم تمثيل العوامل بواسطة مثيلات Actor التي ترث من DataModel. تعمل كوحدات عزل تنفيذية توزع الحمل عبر عدة نوى تعمل في وقت واحد.

وضع مثيلات العوامل

يمكنك وضع العوامل في حاويات مناسبة أو استخدامها لاستبدال أنواع المثيلات على المستوى الأعلى لكياناتك ثلاثية الأبعاد مثل الشخصيات غير القابلة للعب وأجهزة الأشعة، ثم إضافة النصوص المقابلة.

مثال على نص تحت عامل

في معظم الحالات، لا ينبغي عليك وضع عامل كطفل لعامل آخر في نموذج البيانات. ومع ذلك، إذا قررت وضع نص متداخل ضمن عدة عوامل لحالتك الخاصة، فإن النص يكون مملوكًا لأقرب عامل له.

شجرة من العوامل والنصوص توضح كيف يمتلك النص أقرب عامل له

عدم تزامن الخيوط

على الرغم من أن وضع النصوص تحت العوامل يمنحها القدرة على التنفيذ المتوازي، إلا أن الكود لا يزال يعمل بشكل تسلسلي على خيط واحد بشكل افتراضي، مما لا يحسن أداء وقت التشغيل. تحتاج إلى استدعاء task.desynchronize()، وهي دالة قابلة للتوقف تعطل تنفيذ الكوروتين الحالي لتشغيل الكود بشكل متوازي وتستأنف عند الفرصة التالية للتنفيذ المتوازي. لتبديل النص إلى التنفيذ التسلسلي، استدعِ task.synchronize().

بدلاً من ذلك، يمكنك استخدام طريقة RBXScriptSignal:ConnectParallel() عندما تريد جدولة رد نداء إشارة لتشغيل كودك على الفور بشكل متوازي عند التفعيل. لا تحتاج إلى استدعاء task.desynchronize() داخل رد نداء الإشارة.

عدم تزامن خيط
local RunService = game:GetService("RunService")
RunService.Heartbeat:ConnectParallel(function()
... -- بعض الكود المتوازي الذي يحسب تحديث الحالة
task.synchronize()
... -- بعض الكود التسلسلي الذي يغير حالة الكائنات
end)

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

كود متوازي في عوامل تعمل تسلسليًا على خيط واحد
كود متوازي في عوامل تعمل في وقت واحد على خيوط متعددة

أمان الخيوط

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

تمتلك أعضاء واجهة برمجة التطبيقات مستوى أمان خيوط يشير إلى ما إذا كان يمكنك استخدامها في كودك المتوازي وكيفية استخدامها، كما هو موضح في الجدول التالي:

مستوى الأمانبالنسبة للخصائصبالنسبة للدوال
غير آمنلا يمكن قراءته أو كتابته في الوضع المتوازي.لا يمكن استدعاؤه في الوضع المتوازي.
قراءة متوازيةيمكن قراءته ولكن لا يمكن كتابته في الوضع المتوازي.N/A
آمن محليًايمكن استخدامه ضمن نفس العامل؛ يمكن قراءته ولكن لا يمكن كتابته بواسطة عوامل Actors الأخرى في الوضع المتوازي.يمكن استدعاؤه ضمن نفس العامل؛ لا يمكن استدعاؤه بواسطة عوامل Actors الأخرى في الوضع المتوازي.
آمنيمكن قراءته وكتابته.يمكن استدعاؤه.

يمكنك العثور على علامات أمان الخيوط لأعضاء واجهة برمجة التطبيقات في مرجع واجهة برمجة التطبيقات. عند استخدامها، يجب عليك أيضًا مراعاة كيفية تفاعل استدعاءات واجهة برمجة التطبيقات أو تغييرات الخصائص بين الخيوط المتوازية. عادةً ما يكون من الآمن أن تقرأ عوامل متعددة نفس البيانات مثل عوامل أخرى ولكن لا تعدل حالة عوامل أخرى.

التواصل عبر الخيوط

في سياق تعدد الخيوط، يمكنك أيضًا السماح للنصوص في عوامل مختلفة بالتواصل مع بعضها البعض لتبادل البيانات، وتنسيق المهام، ومزامنة الأنشطة. يدعم المحرك الآليات التالية للتواصل عبر الخيوط:

يمكنك دعم آليات متعددة لتلبية احتياجات التواصل عبر الخيوط الخاصة بك. على سبيل المثال، يمكنك إرسال جدول مشترك من خلال واجهة برمجة التطبيقات لرسائل العوامل.

رسائل العوامل

تسمح واجهة برمجة التطبيقات لرسائل العوامل لنص، سواء في سياق تسلسلي أو متوازي، بإرسال بيانات إلى عامل في نفس نموذج البيانات. التواصل من خلال هذه الواجهة غير متزامن، حيث لا يقوم المرسل بحظر التنفيذ حتى يتلقى المستلم الرسالة.

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

تحتوي الواجهة على الطرق التالية:

يوضح المثال التالي كيفية استخدام Actor:SendMessage() لتحديد موضوع وإرسال رسالة من جانب المرسل:

مثال على مرسل الرسالة
local Workspace = game:GetService("Workspace")
-- إرسال رسالتين إلى عامل العامل بموضوع "تحية"
local workerActor = Workspace.WorkerActor
workerActor:SendMessage("Greeting", "مرحبا بالعالم!")
workerActor:SendMessage("Greeting", "أهلاً وسهلاً")
print("تم إرسال الرسائل")

يوضح المثال التالي كيفية استخدام Actor:BindToMessageParallel() لربط رد نداء لموضوع معين في سياق متوازي من جانب المستلم:

مثال على مستلم الرسالة
-- الحصول على العامل الذي يرتبط به هذا النص
local actor = script:GetActor()
-- ربط رد نداء لموضوع الرسالة "تحية"
actor:BindToMessageParallel("Greeting", function(greetingString)
print(actor.Name, "-", greetingString)
end)
print("تم الربط بالرسائل")

جدول مشترك

SharedTable هو هيكل بيانات يشبه الجدول يمكن الوصول إليه من النصوص التي تعمل تحت عدة عوامل. إنه مفيد في الحالات التي تتضمن كمية كبيرة من البيانات وتتطلب حالة مشتركة بين عدة خيوط. على سبيل المثال، عندما تعمل عدة عوامل على حالة عالم مشترك لا يتم تخزينها في نموذج البيانات.

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

التواصل المباشر مع نموذج البيانات

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

أمثلة

التحقق من صحة الأشعة على جانب الخادم

لعبة قتال ومعركة، تحتاج إلى تمكين الأشعة لأسلحة مستخدميك. مع محاكاة العميل للأسلحة لتحقيق زمن انتقال جيد، يجب على الخادم تأكيد الإصابة، مما يتطلب القيام بأشعة وبعض الحسابات التي تحسب سرعة الشخصية المتوقعة، والنظر في السلوك السابق.

بدلاً من استخدام نص مركزي واحد يتصل بحدث عن بُعد يستخدمه العملاء للتواصل بمعلومات الإصابة، يمكنك تشغيل كل عملية تحقق من الإصابة على جانب الخادم بشكل متوازي مع كل شخصية مستخدم لديها حدث عن بُعد منفصل.

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

local Workspace = game:GetService("Workspace")
local tool = script.Parent.Parent
local remoteEvent = Instance.new("RemoteEvent") -- إنشاء حدث عن بُعد جديد وإضافته إلى الأداة
remoteEvent.Name = "RemoteMouseEvent" -- إعادة تسميته بحيث يمكن للنص المحلي البحث عنه
remoteEvent.Parent = tool
local remoteEventConnection -- إنشاء مرجع لاتصال الحدث عن بُعد
-- دالة تستمع لحدث عن بُعد
local function onRemoteMouseEvent(player: Player, clickLocation: CFrame)
-- تسلسلي: تنفيذ كود الإعداد بشكل تسلسلي
local character = player.Character
-- تجاهل شخصية المستخدم أثناء إجراء الأشعة
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = { character }
-- متوازي: إجراء الأشعة بشكل متوازي
task.desynchronize()
local origin = tool.Handle.CFrame.Position
local epsilon = 0.01 -- يستخدم لتمديد الأشعة قليلاً حيث قد يكون موقع النقر بعيدًا قليلاً عن الكائن
local lookDirection = (1 + epsilon) * (clickLocation.Position - origin)
local raycastResult = Workspace:Raycast(origin, lookDirection, params)
if raycastResult then
local hitPart = raycastResult.Instance
if hitPart and hitPart.Name == "block" then
local explosion = Instance.new("Explosion")
-- تسلسلي: الكود أدناه يعدل الحالة خارج العامل
task.synchronize()
explosion.DestroyJointRadiusPercent = 0 -- جعل الانفجار غير قاتل
explosion.Position = clickLocation.Position
-- يمكن أن تحصل عوامل متعددة على نفس الجزء في الأشعة وتقرر تدميره
-- هذا آمن تمامًا ولكنه سيؤدي إلى حدوث انفجارين في وقت واحد بدلاً من واحد
-- يتحقق ما يلي من أن التنفيذ وصل إلى هذا الجزء أولاً
if hitPart.Parent then
explosion.Parent = Workspace
hitPart:Destroy() -- تدميره
end
end
end
end
-- الاتصال بالإشارة بشكل تسلسلي في البداية حيث أن بعض كود الإعداد غير قادر على التشغيل بشكل متوازي
remoteEventConnection = remoteEvent.OnServerEvent:Connect(onRemoteMouseEvent)

توليد التضاريس الإجرائية على جانب الخادم

لإنشاء عالم شاسع للعبتك، يمكنك ملء العالم ديناميكيًا. عادةً ما ينشئ التوليد الإجرائي قطع تضاريس مستقلة، حيث يقوم المولد بإجراء حسابات معقدة نسبيًا لوضع الكائنات، واستخدام المواد، وملء الفوكسل. يمكن أن يعزز تشغيل كود التوليد بشكل متوازي كفاءة العملية. يعد المثال التالي كودًا توضيحيًا.

-- يتطلب التنفيذ المتوازي استخدام العوامل
-- يقوم هذا النص باستنساخ نفسه؛ يبدأ الأصل العملية، بينما تعمل النسخ كعمال
local Workspace = game:GetService("Workspace")
local actor = script:GetActor()
if actor == nil then
local workers = {}
for i = 1, 32 do
local actor = Instance.new("Actor")
script:Clone().Parent = actor
table.insert(workers, actor)
end
-- إضافة جميع العوامل تحت الذات
for _, actor in workers do
actor.Parent = script
end
-- توجيه العوامل لتوليد التضاريس عن طريق إرسال رسائل
-- في هذا المثال، يتم اختيار العوامل عشوائيًا
task.defer(function()
local rand = Random.new()
local seed = rand:NextNumber()
local sz = 10
for x = -sz, sz do
for y = -sz, sz do
for z = -sz, sz do
workers[rand:NextInteger(1, #workers)]:SendMessage("GenerateChunk", x, y, z, seed)
end
end
end
end)
-- الخروج من النص الأصلي؛ يتم تشغيل بقية الكود في كل عامل
return
end
function makeNdArray(numDim, size, elemValue)
if numDim == 0 then
return elemValue
end
local result = {}
for i = 1, size do
result[i] = makeNdArray(numDim - 1, size, elemValue)
end
return result
end
function generateVoxelsWithSeed(xd, yd, zd, seed)
local matEnums = {Enum.Material.CrackedLava, Enum.Material.Basalt, Enum.Material.Asphalt}
local materials = makeNdArray(3, 4, Enum.Material.CrackedLava)
local occupancy = makeNdArray(3, 4, 1)
local rand = Random.new()
for x = 0, 3 do
for y = 0, 3 do
for z = 0, 3 do
occupancy[x + 1][y + 1][z + 1] = math.noise(xd + 0.25 * x, yd + 0.25 * y, zd + 0.25 * z)
materials[x + 1][y + 1][z + 1] = matEnums[rand:NextInteger(1, #matEnums)]
end
end
end
return {materials = materials, occupancy = occupancy}
end
-- ربط رد نداء ليتم استدعاؤه في سياق التنفيذ المتوازي
actor:BindToMessageParallel("GenerateChunk", function(x, y, z, seed)
local voxels = generateVoxelsWithSeed(x, y, z, seed)
local corner = Vector3.new(x * 16, y * 16, z * 16)
-- حاليًا، يجب استدعاء WriteVoxels() في المرحلة التسلسلية
task.synchronize()
Workspace.Terrain:WriteVoxels(
Region3.new(corner, corner + Vector3.new(16, 16, 16)),
4,
voxels.materials,
voxels.occupancy
)
end)

أفضل الممارسات

لتطبيق أقصى فوائد من البرمجة المتوازية، راجع أفضل الممارسات التالية عند إضافة كود لوا الخاص بك:

  • تجنب الحسابات الطويلة — حتى في الوضع المتوازي، يمكن أن تؤدي الحسابات الطويلة إلى حظر تنفيذ نصوص أخرى وتسبب تأخيرًا. تجنب استخدام البرمجة المتوازية للتعامل مع حجم كبير من الحسابات الطويلة التي لا يمكن إيقافها.

    مخطط يوضح كيف يمكن أن يؤدي تحميل مرحلة التنفيذ المتوازي إلى تأخير
  • استخدم العدد الصحيح من العوامل — للحصول على أفضل أداء، استخدم المزيد من Actors. حتى إذا كان الجهاز يحتوي على نوى أقل من Actors، فإن الدقة تسمح بتوازن تحميل أكثر كفاءة بين النوى.

    عرض يوضح كيف أن استخدام المزيد من العوامل يوازن الحمل عبر النوى

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

©2026 شركة Roblox Corporation. تُعد منصّة Roblox، وشعار Roblox وشعار "توسيع حدود المخيلة"، من ضمن علاماتنا التجارية المسجّلة وغير المسجّلة في الولايات المتحدة وبلدان أخرى.