Luau Paralel

*Konten ini diterjemahkan menggunakan AI (Beta) dan mungkin mengandung kesalahan. Untuk melihat halaman ini dalam bahasa Inggris, klik di sini.

Dengan Luau Paralel Luau model pemrograman, Anda dapat mengeksekusan kode pada lebih dari satu thread sekaligus, yang dapat meningkatkan kinerja pengalaman Anda. Saat Anda mengekspansi pengalaman Anda dengan lebih banyak konten, Anda dapat mengadopsi model ini untuk membantu menjaga kinerja dan keamanan skrip Luau Anda.

Model Pemrograman Paralel

Secara default, skrip eksekusi berurutan. Jika pengalaman Anda memiliki logika atau konten yang rumit, seperti karakter non-pemain (NPC), validasi raycasting, dan generasi prosedural, maka eksekusi berurutan mungkin menyebabkan kelambatan bagi pengguna Anda. Dengan model pemrograman paralel, Anda dapat membagi tugas menjadi beberapa script dan mengeksekutakann

Model pemrograman paralel juga menambahkan manfaat keamanan untuk kode Anda. Dengan membagi kode menjadi beberapa subprocess, ketika Anda mengedit kode di subprocess satu, itu tidak meng影响其他 kode yang berjalan secara paralel. Ini mengurangi risiko memiliki satu bug dalam kode Anda yang mengganggu seluruh pengalaman, dan mengurangi ketertinggalan bagi pengguna di server live saat Anda menekan update.

Mengadopsi model pemrograman paralel tidak berarti menempatkan segalanya dalam beberapa thread. Misalnya, Server-side Raycasting Validation menetapkan setiap pengguna individual acara remote secara paralel tetapi masih mengharuskan kode awal untuk dijalankan secara seri untuk mengubah proporsi global, yang merupakan pola umum untuk eksekusi paralel.

Sebagian besar waktu Anda perlu menggabungkan fase seri dan paralel untuk mencapai hasil yang diinginkan, karena saat ini ada beberapa operasi yang tidak didukung secara paralel yang dapat mencegah eksekusi skrip, seperti mengubah instans dalam fase paralel. Untuk lebih banyak informasi tentang tingkat penggunaan API dalam paralel, lihat KeselamatanThread.

Membagi Kode Menjadi Beberapa Thread

Untuk mengeksekusi skrip pengalaman Anda dalam beberapa thread secara bersamaan, Anda perlu membaginya menjadi potongan logik di bawah berbagai aktor di model data . Aktor diwakili oleh Actor instans yang mewarisi dari 1> Class.DataModel1> . Mereka bekerja sebagai unit isolasi eksekus

Menempatkan Instansi Aktor

Anda dapat menempatkan aktor di wadah yang sesuai atau menggunakannya untuk mengganti jenis instans tingkat atas dari entitas 3D Anda seperti NPC dan raycasters, lalu tambahkan skrip yang sesuai.

An example of a Script under an Actor

Untuk kebanyakan situasi, Anda seharusnya tidak menempatkan aktor sebagai anak aktor lain di model data. Namun, jika Anda memutuskan untuk menempatkan skrip berantai di banyak aktor untuk kasus penggunaan spesifik Anda, skrip dimiliki oleh aktor leluhur terdekat.

A tree of actors and scripts that shows how a script is owned by its closest actor

Menghubungkan Thread

Meskipun menempatkan skrip di bawah aktor memberi mereka kemampuan untuk eksekusi paralel, secara default kode masih dijalankan di thread tunggal secara seri, yang tidak meningkatkan kinerja runtime. Anda perlu menelepon fungsi task.desynchronize(), yang menyuspend eksekusi coroutine saat ini untuk mengeksekkan

Alternatifnya, Anda dapat menggunakan metode RBXScriptSignal:ConnectParallel() ketika Anda ingin menjadwalkan panggilan sinyal untuk segera mengeksekusi kode Anda secara paralel setelah memicu. Anda tidak perlu menelepon task.desynchronize() di dalam panggilan sinyal.

Non-sinkronisasi Thread

local RunService = game:GetService("RunService")
RunService.Heartbeat:ConnectParallel(function()
... -- Beberapa kode paralel yang menghitung pembaruan update
task.synchronize()
... -- Beberapa kode seri yang mengubah status instans
end)

Skrip yang merupakan bagian dari aktor yang sama selalu dijalankan secara berurutan dengan satu sama lain, jadi Anda memerlukan beberapa aktor. Misalnya, jika Anda menempatkan semua naskah perilaku yang diaktifkan paralel untuk NPC Anda dalam satu aktor, mereka masih dijalankan secara seri di satu thread, tetapi jika Anda memiliki beberapa aktor untuk logika NPC

Kode paralel di Aktor yang berjalan secara seri dalam satu thread tunggal
Kode paralel dalam Aktor berjalan bersamaan dalam lebih dari satu thread.

KeselamatanThread

Selama eksekusi paralel, Anda dapat mengakses kebanyakan instans dari DataModel hierarki seperti biasa, tetapi beberapa API property dan fungsi tidak aman untuk dibaca atau ditulis. Jika Anda menggunakannya dalam kode paralel Anda, mesin Roblox dapat secara otomatis mendeteksi dan menghindari jenis akses ini terjadi.

Anggota API memiliki tingkat keamanan subprocess yang menunjukkan apakah dan bagaimana Anda dapat menggunakannya dalam kode paralel Anda, seperti yang ditunjukkan oleh tabel berikut:

Tingkat KeamananUntuk PropertiUntuk Fungsi
Tidak Aman Tidak dapat dibaca atau ditulis secara paralel.Tidak dapat dipanggil secara paralel.
Baca Paralel Dapat dibaca tetapi tidak ditulis secara paralel.Tidak ada
Dompet Lokal Dapat digunakan dalam Actor yang sama; dapat dibaca tetapi tidak dapat ditulis oleh Actors lainnya secara paralel.Dapat dipanggil dalam waktu yang sama Actor; tidak dapat dipanggil oleh Actors lainnya secara paralel.
Aman Dapat dibaca dan ditulis.Bisa dipanggil.

Anda dapat menemukan label keamanan subprocess untuk anggota API di referensi API. Saat menggunakannya, Anda juga harus mempertimbangkan cara panggilan API atau perubahan properti mungkin berinteraksi di antara subprocess paralel. Biasanya aman bagi beberapa aktor untuk membaca data yang sama dengan aktor lain tetapi tidak mengubah status dari aktor lain.

Komunikasi Antara Subprocess

Dalam konteks multithreading, Anda masih dapat mengizinkan skrip di berbagai aktor untuk berkomunikasi satu sama lain untuk menukar data, menkoordinasikan tugas, dan menyinkronisasi aktivitas. Engine mendukung berbagai mekanisme untuk komunikasi antithread:

  • API untuk Mengirim Pesan ke Aktor Menggunakan Skrip.
  • Tabel Berbagi data struktur untuk berbagi jumlah besar data di antara beberapa aktor dalam negara bagian bersama.
  • Komunikasi Model Data Langsung untuk komunikasi sederhana dengan keterbatasan.

Anda dapat mendukung beberapa mekanisme untuk menangani kebutuhan komunikasi antara sub-Thread Anda. Misalnya, Anda dapat mengirim tabel bersama melalui Actor Messaging API.

Pesan Aktor

Aktor Messaging API mengizinkan script, baik dalam konteks seri atau paralel, untuk mengirim data ke aktor dalam model data yang sama. Komunikasi melalui API ini adalah asinkron, di mana pengirim tidak memblokir sampai penerima menerima pesan.

Saat mengirim pesan menggunakan API ini, Anda perlu mendefinisikan topik untuk kategori pesan untuk mengkategorikan pesan. Setiap pesan hanya dapat dikirim ke satu aktor, tetapi aktor itu dapat secara internal memiliki beberapa panggilan yang terikat pada pesan. Hanya skrip yang merupakan pendahulu aktor dapat menerima pesan.

API memiliki metode berikut:

Contoh berikut menunjukkan cara menggunakan Actor:SendMessage() untuk mendefinisikan topik dan mengirim pesan di akhiripengirim:

Pengirim Pesan Contoh

-- Kirim dua pesan ke aktor pekerja dengan topik "Pelukan"
local workerActor = workspace.WorkerActor
workerActor:SendMessage("Greeting", "Hello World!")
workerActor:SendMessage("Greeting", "Welcome")
print("Sent messages")

Contoh berikut menunjukkan cara menggunakan Actor:BindToMessageParallel() untuk menyambungkan panggilan kembali untuk topik tertentu dalam konteks paralel pada akhiripenerima:

Penerima Pesan Contoh

-- Dapatkan aktor ke mana naskah ini dianggap
local actor = script:GetActor()
-- Ikat panggilan untuk topik pesan "Selamat Datang"
actor:BindToMessageParallel("Greeting", function(greetingString)
print(actor.Name, "-", greetingString)
end)
print("Bound to messages")

Tabel Berbagi

SharedTable adalah struktur data seperti tabel yang diakses dari skrip yang berjalan di bawah beberapa aktor. Ini berguna untuk situasi yang melibatkan banyak data dan memerlukan negara bagian bersama yang umum di antara beberapa thread. Misalnya, ketika beberapa aktor bekerja pada negara dunia yang tidak disimpan dalam model data.

Mengirim tabel shared ke aktor lain tidak membuat salinan data. Sebaliknya, tabel shared memungkinkan update yang aman dan atomik oleh banyak script secara bersamaan. Setiap update ke tabel shared oleh satu aktor langsung terlihat oleh semua aktor. Tabel shared juga dapat diklon dalam proses yang efisien secara资源, yang menggunakan shared sharing daripada mengkopi data yang berada di bawahnya.

Komunikasi Langsung Model Data

Anda juga dapat memudahkan komunikasi antara beberapa thread langsung menggunakan model data, di mana berbagai aktor dapat menulis dan kemudian membaca propperti atau属性. Namun, untuk menjaga keamanan thread, skrip yang dijalankan secara paralel umumnya tidak dapat menulis ke model data. Jadi, menggunakan langsung model data untuk komunikasi datang dengan keterbatasan dan dapat memaksa skrip untuk disinkronisasi deng

Contoh

Validasi Raycasting Sisi Server

Untuk pengalaman bertarung dan pertempuran, Anda perlu mengaktifkan raycasting untuk senjata pengguna Anda. Dengan klien menyimulasikan senjata untuk mencapai latensi yang baik, server harus mengkonfirmasi hit, yang melibatkan melakukan raycast dan beberapa jumlah heuristik yang menghitung kecepatan karakter yang diharapkan, dan melihat ke perilaku masa lalu.

Alih-alih menggunakan satu naskah pusat yang terhubung ke acara remote yang dikonsumsi klien untuk berkomunikasi dengan informasi hit, Anda dapat mengeksekusi setiap proses validasi hit di sisi server secara paralel dengan setiap karakter pengguna yang memiliki acara remote yang berpisah.

Skrip sisi server yang dijalankan di bawah karakter tersebut Actor terhubung ke acara remote ini menggunakan koneksi paralel untuk mengeksekusi logika yang relevan untuk mengkonfirmasi hit. Jika logika menemukan konfirmasi hit, kerusakan diambil, yang melibatkan mengubah prop, jadi itu dijalankan secara seri pada awalnya.


local tool = script.Parent.Parent
local remoteEvent = Instance.new("RemoteEvent") -- Buat acara remote baru dan isi alat
remoteEvent.Name = "RemoteMouseEvent" -- Ganti namanya agar lokasi skrip lokal dapat mencarinya
remoteEvent.Parent = tool
local remoteEventConnection -- Buat referensi untuk koneksi acara remote
-- Fungsi yang mendengarkan acara remote
local function onRemoteMouseEvent(player: Player, clickLocation: CFrame)
-- SERIAL: Eksekusikan kode pengaturan dalam serial
local character = player.Character
-- Abaikan karakter pengguna saat raycasting
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = { character }
-- PARALEL: Lakukan raycast secara paralel
task.desynchronize()
local origin = tool.Handle.CFrame.Position
local epsilon = 0.01 -- Digunakan untuk menambahkan sedikit sinar karena lokasi klik mungkin sedikit berbeda dari objek
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")
-- SERIAL: Kode di bawah ini mengubah status di luar aktor
task.synchronize()
explosion.DestroyJointRadiusPercent = 0 -- Jadikan ledakan tidak mematikan
explosion.Position = clickLocation.Position
-- Beberapa aktor dapat mendapatkan bagian yang sama dalam raycast dan memutuskan untuk menghancurkannya
-- Ini benar-benar aman tetapi akan menyebabkan dua ledakan sekaligus bukan satu
-- Langkah berikut memeriksa dua kali apakah eksekusi mendapatkan bagian ini terlebih dahulu
if hitPart.Parent then
explosion.Parent = workspace
hitPart:Destroy() -- Hancurkan itu
end
end
end
end
-- Hubungkan sinyal secara seri pada awalnya karena beberapa kode pengaturan tidak dapat dijalankan secara paralel
remoteEventConnection = remoteEvent.OnServerEvent:Connect(onRemoteMouseEvent)

Pembuatan Tanah Prosedural Sisi Server

Untuk membuat dunia yang luas untuk pengalaman Anda, Anda dapat mengisi dunia secara dinamis. Pembuatan kode biasanya menghasilkan potongan tanah yang lebih kecil, dengan generator melakukan perhitungan yang relatif rumit untuk tempat penempatan objek, penggunaan bahan, dan pengisian voxel. Mengeksekusi kode pembuatan secara paralel dapat meningkatkan efisiensi proses. Berikut adalah contoh kode.


-- Eksekusi paralel memerlukan penggunaan aktor
-- Skrip ini mengkloning dirinya sendiri; yang asli menginisialisasi proses, sementara klon bertindak sebagai pekerja
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
-- Orang tua semua aktor di bawah diri sendiri
for _, actor in workers do
actor.Parent = script
end
-- Instruksi aktor untuk menghasilkan terreno dengan mengirim pesan
-- Dalam contoh ini, aktor dipilih secara acak
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)
-- Keluar dari script asli; sisa kode berjalan di setiap aktor
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
-- Ikat panggilan untuk dipanggil dalam konteks eksekusi paralel
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)
-- Saat ini, WriteVoxels() harus dipanggil dalam fase seri
task.synchronize()
workspace.Terrain:WriteVoxels(
Region3.new(corner, corner + Vector3.new(16, 16, 16)),
4,
voxels.materials,
voxels.occupancy
)
end)

Praktik Terbaik

Untuk menerapkan manfaat maksimal pemrograman paralel, mengacu pada praktik terbaik berikut saat menambahkan kode Lua Anda:

  • Hindari Hitungan Panjang — Bahkan dalam paralel, panjang hitungan dapat menghentikan eksekusi script lain dan menyebabkan kelambatan. Hindari menggunakan pemrograman paralel untuk menangani volume besar dari panjang pemrosesan yang tak terputus.

    Diagram demonstrating how overloading the parallel execution phase can still cause lag
  • Gunakan Jumlah Aktor yang Benar — Untuk pelaksanaanterbaik, gunakan lebih banyak Actors . Bahkan jika perangkat memiliki lebih sedikit core daripada Actors, granularitas memungkinkan lebih banyak pengalokan load balancing antara core.

    Demonstration of how using more actors balances the load across cores

    Ini tidak berarti Anda harus menggunakan seb