Dengan model pemrograman Parallel Luau , Anda dapat menjalankan kode di banyak thread secara bersamaan, yang dapat meningkatkan kinerja pengalaman Anda.Saat Anda memperluas 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 dieksekusi secara berurutan.Jika pengalaman Anda memiliki logika atau konten yang kompleks, 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 skrip dan menjalankannya secara paralel.Ini membuat kode pengalaman Anda berjalan lebih cepat, yang meningkatkan pengalaman pengguna.
Model pemrograman paralel juga menambahkan manfaat keselamatan ke kode Anda.Dengan membagi kode menjadi beberapa thread, saat Anda mengedit kode di satu thread, itu tidak memengaruhi kode lain yang berjalan secara paralel.Ini mengurangi risiko memiliki satu bug di kode Anda yang merusak seluruh pengalaman, dan mengurangi kelambatan bagi pengguna di server langsung saat Anda mendorong update.
Mengadopsi model pemrograman paralel tidak berarti menempatkan semuanya di banyak thread.Sebagai contoh, validasi raycasting sisi server menetapkan setiap pengguna individu acara jarak jauh secara paralel tetapi masih memerlukan kode awal untuk dijalankan secara seri untuk mengubah properti global, yang merupakan pola umum untuk eksekusi paralel.
Sebagian besar waktu Anda perlu menggabungkan fase seri dan paralel untuk mencapai output yang diinginkan, karena saat ini ada beberapa operasi yang tidak didukung secara paralel yang dapat mencegah eksekusi skrip, seperti mengubah instans di fase paralel.Untuk informasi lebih lanjut tentang tingkat penggunaan API secara paralel, lihat keselamatan thread.
Membagi kode menjadi beberapa thread
Untuk menjalankan skrip pengalaman Anda di banyak thread secara bersamaan, Anda perlu membaginya menjadi potongan logis di bawah aktor berbeda di model data .Aktor diwakili oleh instansi Actor yang mewarisi dari DataModel .Mereka bekerja sebagai unit isolasi eksekusi yang mendistribusikan beban di antara banyak core yang berjalan secara bersamaan.
Tempatkan instansi aktor
Anda dapat menempatkan aktor di wadah yang tepat atau menggunakannya untuk menggantikan jenis instansi tingkat atas dari entitas 3D Anda seperti NPC dan raycasters, lalu tambahkan skrip yang sesuai.

Untuk sebagian besar situasi, Anda tidak harus menempatkan aktor sebagai anak dari aktor lain dalam model data.Namun, jika Anda memutuskan untuk menempatkan skrip yang disarangkan dalam beberapa aktor untuk kasus penggunaan spesifik Anda, skrip dimiliki oleh aktor leluhur terdekatnya.

Non-sinkronisasi thread
Meskipun menempatkan skrip di bawah aktor memberi mereka kemampuan untuk eksekusi paralel, secara default kode masih dijalankan secara seri pada satu thread, yang tidak meningkatkan pelaksanaanruntime.Anda perlu memanggil task.desynchronize(), fungsi yang dapat diberikan yang menangguhkan eksekusi coroutine saat ini untuk menjalankan kode secara paralel dan melanjutkannya pada kesempatan eksekusi paralel berikutnya.Untuk beralih skrip kembali ke eksekusi seri, panggil task.synchronize() .
Alternatifnya, Anda dapat menggunakan metode RBXScriptSignal:ConnectParallel() ketika ingin menjadwalkan panggilan balas sinyal untuk segera menjalankan kode paralel Anda saat diaktifkan.Anda tidak perlu memanggil task.desynchronize() di dalam panggilan balas sinyal.
Desinkronisasi 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 dieksekusi secara berurutan terhadap satu sama lain, sehingga Anda membutuhkan banyak aktor.Sebagai contoh, jika Anda menempatkan semua skrip perilaku yang diaktifkan paralel untuk NPC Anda di satu aktor, mereka masih berjalan secara seri di satu thread, tetapi jika Anda memiliki banyak aktor untuk logika NPC yang berbeda, masing-masing berjalan secara paralel di threadnya sendiri.Untuk informasi lebih lanjut, lihat Praktik terbaik.


Keamanan thread
Selama eksekusi paralel, Anda dapat mengakses sebagian besar instans dari hierarki DataModel seperti biasa, tetapi beberapa properti dan fungsi API tidak aman untuk dibaca atau ditulis.Jika Anda menggunakannya dalam kode paralel Anda, Roblox Engine dapat secara otomatis mendeteksi dan mencegah akses ini terjadi.
Anggota API memiliki tingkat keamanan thread yang menunjukkan apakah dan bagaimana Anda dapat menggunakannya dalam kode paralel, seperti tabel berikut menunjukkan:
levelkeamanan | Untuk property | Untuk fungsi |
---|---|---|
Tidak Aman | Tidak dapat dibaca atau ditulis secara paralel. | Tidak dapat dipanggil secara paralel. |
Membaca Paralel | Bisa dibaca tetapi tidak ditulis secara paralel. | N/A |
Lemari Besi Lokal | Dapat digunakan dalam Aktor yang sama; dapat dibaca tetapi tidak ditulis oleh yang lain Actors secara paralel. | Dapat dipanggil dalam Aktor yang sama; tidak dapat dipanggil oleh yang lain Actors secara paralel. |
Amankah | Dapat dibaca dan ditulis. | Dapat dipanggil. |
Anda dapat menemukan tag keamanan thread untuk anggota API di referensi API.Saat menggunakannya, Anda juga harus mempertimbangkan bagaimana panggilan API atau perubahan properti dapat berinteraksi di antara thread paralel.Biasanya aman bagi banyak aktor untuk membaca data yang sama dengan aktor lain tetapi tidak mengubah status aktor lain.
Komunikasi antar thread
Dalam konteks multithreading, Anda masih dapat mengizinkan skrip di aktor yang berbeda untuk berkomunikasi satu sama lain untuk bertukar data, mengkoordinasikan tugas, dan menyinkronkan aktivitas.Mesin mendukung mekanisme berikut untuk komunikasi antar thread:
- Pesan aktor API untuk mengirim pesan ke aktor menggunakan skrip.
- Struktur data tabel berbagi untuk berbagi secara efisien jumlah besar data antara banyak aktor di negara bersama.
- Komunikasi model data langsung untuk komunikasi sederhana dengan batasan.
Anda dapat mendukung beberapa mekanisme untuk memenuhi kebutuhan komunikasi antar thread Anda.Sebagai contoh, Anda dapat mengirim tabel bersama melalui API Pesan Aktor.
Pesan aktor
API pesan aktor memungkinkan sebuah skrip, baik dalam konteks seri atau paralel, untuk mengirim data ke aktor di model data yang sama.Komunikasi melalui API ini asinkron, di mana pengirim tidak memblokir sampai penerima menerima pesan.
Saat mengirim pesan menggunakan API ini, Anda perlu mendefinisikan topik untuk mengkategorikan pesan.Setiap pesan hanya dapat dikirim ke satu aktor, tetapi aktor itu dapat secara internal memiliki banyak panggilan balas yang terikat pada pesan.Hanya skrip yang merupakan keturunan aktor dapat menerima pesan.
API memiliki metode berikut:
- Actor:SendMessage() untuk mengirim pesan ke aktor.
- Actor:BindToMessage() untuk mengikat panggilan balik Luau ke pesan dengan topik yang ditentukan dalam konteks seri.
- Actor:BindToMessageParallel() untuk mengikat panggilan balik Luau ke pesan dengan topik yang ditentukan dalam konteks paralel.
Contoh berikut menunjukkan cara menggunakan Actor:SendMessage() untuk mendefinisikan topik dan mengirim pesan di akhiripengirim:
Contoh Pengirim Pesan
local Workspace = game:GetService("Workspace")-- Kirim dua pesan ke aktor pekerja dengan topik "Salam"local workerActor = Workspace.WorkerActorworkerActor:SendMessage("Greeting", "Hello World!")workerActor:SendMessage("Greeting", "Welcome")print("Sent messages")
Contoh berikut menunjukkan cara menggunakan Actor:BindToMessageParallel() untuk mengikat panggil balik untuk topik tertentu dalam konteks paralel di akhiripenerima:
Penerima Pesan Contoh
-- Dapatkan aktor yang skrip ini diberikan kepada orang tuanya
local actor = script:GetActor()
-- Bind sebuah callback untuk topik pesan "Salam"
actor:BindToMessageParallel("Greeting", function(greetingString)
print(actor.Name, "-", greetingString)
end)
print("Bound to messages")
Tabel berbagi
SharedTable adalah struktur data berupa tabel yang dapat diakses dari skrip yang berjalan di bawah beberapa aktor.Ini berguna untuk situasi yang melibatkan banyak data dan memerlukan negara bersama umum di antara banyak thread.Sebagai contoh, ketika beberapa aktor bekerja pada negara dunia bersama yang tidak disimpan dalam model data.
Mengirim tabel bersama ke aktor lain tidak membuat salinan data.Sebagai gantinya, tabel bersama memungkinkan pembaruan aman dan atomik oleh beberapa skrip secara bersamaan.Setiap pembaruan ke tabel bersama oleh satu aktor segera terlihat oleh semua aktor.Tabel berbagi juga dapat diklon dalam proses yang efisien sumber daya yang menggunakan berbagi struktural alih-alih menyalin data dasar.
Komunikasi model data langsung
Anda juga dapat memudahkan komunikasi antara beberapa thread langsung menggunakan model data, di mana aktor yang berbeda dapat menulis dan kemudian membaca properti atau atribut.Namun, untuk menjaga keamanan thread, skrip yang berjalan secara paralel umumnya tidak dapat menulis ke model data.Jadi langsung menggunakan model data untuk komunikasi datang dengan batasan dan dapat memaksa skrip untuk disinkronkan sering, yang dapat memengaruhi kinerja skrip Anda.
Contoh
Validasi raycasting sisi server
Untuk pengalaman pertempuran dan pertempuran, Anda perlu mengaktifkan raycasting untuk senjata pengguna Anda.Dengan klien yang meniru senjata untuk mencapai latensi yang baik, server harus mengkonfirmasi pukulan, yang melibatkan melakukan raycast dan beberapa heuristik yang menghitung kecepatan karakter yang diharapkan, dan melihat perilaku masa lalu.
Alih-alih menggunakan skrip tunggal yang terpusat yang terhubung ke acara remote yang digunakan klien untuk berkomunikasi informasi hit, Anda dapat menjalankan setiap proses validasi hit di sisi server secara paralel dengan setiap karakter pengguna memiliki acara remote terpisah.
Skrip sisi server yang dijalankan di bawah karakter itu Actor terhubung ke acara remote ini menggunakan koneksi paralel untuk menjalankan logika relevan untuk mengkonfirmasi hit.Jika logika menemukan konfirmasi pukulan, kerusakan dikurangi, yang melibatkan perubahan properti, sehingga dijalankan secara seri awalnya.
local Workspace = game:GetService("Workspace")
local tool = script.Parent.Parent
local remoteEvent = Instance.new("RemoteEvent") -- Buat acara remote baru dan berikan kepada alat
remoteEvent.Name = "RemoteMouseEvent" -- Ganti namanya sehingga skrip lokal dapat mencarinya
remoteEvent.Parent = tool
local remoteEventConnection -- Buat referensi untuk koneksi acara remote
-- Fungsi yang mendengarkan untuk acara remote
local function onRemoteMouseEvent(player: Player, clickLocation: CFrame)
-- SERIAL: Jalankan kode pengaturan dalam serial
local character = player.Character
-- Abaikan karakter pengguna saat melakukan raycasting
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = { character }
-- PARALEL: Lakukan raycast paralel
task.desynchronize()
local origin = tool.Handle.CFrame.Position
local epsilon = 0.01 -- Digunakan untuk memperpanjang sinar sedikit karena lokasi klik mungkin sedikit bergeser 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
-- Banyak aktor bisa mendapatkan bagian yang sama dalam raycast dan memutuskan untuk menghancurkannya
-- Ini sangat aman tetapi akan menyebabkan dua ledakan sekaligus bukan satu
-- Pemeriksaan ganda berikut memastikan eksekusi mencapai bagian ini terlebih dahulu
if hitPart.Parent then
explosion.Parent = Workspace
hitPart:Destroy() -- Hancurkan itu
end
end
end
end
-- Hubungkan sinyal dalam seri pada awalnya karena beberapa kode pengaturan tidak dapat dijalankan secara paralel
remoteEventConnection = remoteEvent.OnServerEvent:Connect(onRemoteMouseEvent)
Pembuatan medan prosedural sisi server
Untuk membuat dunia luas untuk pengalaman Anda, Anda dapat mengisi dunia secara dinamis.Generasi prosedural biasanya membuat potongan medan independen, dengan generator melakukan perhitungan relatif rumit untuk penempatan objek, penggunaan bahan, dan pengisian voxel.Menjalankan kode generasi secara paralel dapat meningkatkan efisiensi proses.Contoh kode berikut ini berfungsi sebagai contoh.
-- Eksekusi paralel memerlukan penggunaan aktor
-- Skrip ini mengkloning dirinya sendiri; asli memulai proses, sementara klon bertindak sebagai pekerja
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
-- Orangtua semua aktor di bawah diri sendiri
for _, actor in workers do
actor.Parent = script
end
-- Instruksikan aktor untuk menghasilkan medan 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 skrip asli; sisa kode dijalankan 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
-- Ikatkan panggilan balik 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 dari pemrograman paralel, referensikan praktik terbaik berikut saat menambahkan kode Luau Anda:
Hindari Perhitungan Panjang — Bahkan secara paralel, perhitungan panjang dapat memblokir eksekusi skrip lain dan menyebabkan kelambatan.Hindari menggunakan pemrograman paralel untuk menangani volume besar perhitungan panjang dan tidak menyerah.
Gunakan Jumlah Aktor yang Benar — Untuk pelaksanaanterbaik, gunakan lebih banyak Actors .Bahkan jika perangkat memiliki lebih sedikit core daripada Actors, granularitas memungkinkan keseimbangan beban lebih efisien di antara core.
Ini tidak berarti Anda harus menggunakan sebanyak mungkin Actors .Anda masih harus membagi kode menjadi Actors berdasarkan unit logika daripada memecahkan kode dengan logika terhubung ke yang berbeda Actors .Sebagai contoh, jika Anda ingin mengaktifkan validasi raycasting paralel, masuk akal untuk menggunakan 64 Actors dan lebih banyak lagi alih-alih hanya 4, bahkan jika Anda menargetkan sistem 4-core.Ini berharga untuk skalabilitas sistem dan memungkinkannya untuk mendistribusikan pekerjaan berdasarkan kemampuan hardware dasar.Namun, Anda juga tidak boleh menggunakan terlalu banyak Actors , yang sulit untuk dipertahankan.