لوا المتوازي

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

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

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

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

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

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

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

تقسيم الشيفرة إلى خيوط متعددة

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

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

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

مثال على سكربت تحت ممثل

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

شجرة من الممثلين والسكريبتات التي تظهر كيف يتم ملكية السكربت من قبل أقرب ممثل

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

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

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

عدم تزامن خيط

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

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

شيفرة متوازية في ممثلين تعمل تسلسليًا في خيط واحد
شيفرة متوازية في ممثلين تعمل في وقت واحد في خيوط متعددة

سلامة الخيوط

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

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

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

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

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

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

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

رسائل الممثلين

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

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

تتضمن واجهة API الأساليب التالية:

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

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