Luau Parallelo

*Questo contenuto è tradotto usando AI (Beta) e potrebbe contenere errori. Per visualizzare questa pagina in inglese, clicca qui.

Con il modello di programmazione Luau Parallelo, puoi eseguire codice su più thread simultaneamente, il che può migliorare le prestazioni della tua esperienza. Man mano che espandi la tua esperienza con più contenuti, puoi adottare questo modello per mantenere le prestazioni e la sicurezza dei tuoi script Luau.

Modello di programmazione parallela

Per impostazione predefinita, gli script vengono eseguiti sequenzialmente. Se la tua esperienza ha logiche o contenuti complessi, come personaggi non giocatori (NPC), convalida di raycasting e generazione procedurale, l'esecuzione sequenziale potrebbe causare ritardi per i tuoi utenti. Con il modello di programmazione parallela, puoi scomporre i compiti in più script ed eseguirli in parallelo. Questo fa sì che il codice della tua esperienza venga eseguito più velocemente, migliorando l'esperienza utente.

Il modello di programmazione parallela aggiunge anche vantaggi di sicurezza al tuo codice. Suddividendo il codice in più thread, quando modifichi il codice in un thread, non influisce su altri codici eseguiti in parallelo. Ciò riduce il rischio che un bug nel tuo codice corrompa l'intera esperienza e minimizza i ritardi per gli utenti sui server live quando rilasci un aggiornamento.

Adottare il modello di programmazione parallela non significa mettere tutto in più thread. Ad esempio, la convalida di raycasting lato server imposta a ogni singolo utente un evento remoto in parallelo, ma richiede comunque che il codice iniziale venga eseguito in modo seriale per modificare le proprietà globali, che è un modello comune per l'esecuzione parallela.

Nella maggior parte dei casi, devi combinare fasi seriali e parallele per ottenere l'output desiderato, poiché attualmente ci sono alcune operazioni non supportate in parallelo che possono impedire l'esecuzione degli script, come la modifica delle istanze nelle fasi parallele. Per ulteriori informazioni sul livello di utilizzo delle API in parallelo, vedi sicurezza dei thread.

Suddividi il codice in più thread

Per eseguire gli script della tua esperienza in più thread contemporaneamente, devi suddividerli in blocchi logici sotto diversi attori nel modello dei dati. Gli attori sono rappresentati da istanze Actor che ereditano da DataModel. Funzionano come unità di isolamento dell'esecuzione che distribuiscono il carico su più core in esecuzione simultaneamente.

Posiziona le istanze degli attori

Puoi collocare gli attori in contenitori appropriati o usarli per sostituire i tipi di istanza di livello superiore delle tue entità 3D come NPC e raycaster, quindi aggiungere script corrispondenti.

Un esempio di uno Script sotto un Attore

Nella maggior parte delle situazioni, non dovresti mettere un attore come figlio di un altro attore nel modello dei dati. Tuttavia, se decidi di posizionare uno script annidato all'interno di più attori per il tuo caso d'uso specifico, lo script appartiene al suo attore ancestrale più vicino.

Un albero di attori e script che mostra come uno script appartiene al suo attore più vicino

Desincronizza i thread

Sebbene mettere gli script sotto agli attori conferisca loro la capacità di esecuzione parallela, per impostazione predefinita il codice continua a essere eseguito su un singolo thread in modo seriale, il che non migliora le prestazioni di runtime. Devi chiamare task.desynchronize(), una funzione yieldable che sospende l'esecuzione della coroutine corrente per eseguire codice in parallelo e la riprende alla prossima opportunità di esecuzione parallela. Per passare uno script all'esecuzione seriale, chiama task.synchronize().

In alternativa, puoi utilizzare il metodo RBXScriptSignal:ConnectParallel() quando desideri programmare un callback di segnale per eseguire immediatamente il tuo codice in parallelo al momento dell'attivazione. Non è necessario chiamare task.desynchronize() all'interno del callback di segnale.

Desincronizza un Thread

local RunService = game:GetService("RunService")
RunService.Heartbeat:ConnectParallel(function()
... -- Alcuni codici paralleli che calcolano un aggiornamento di stato
task.synchronize()
... -- Alcuni codici seriali che modificano lo stato delle istanze
end)

Gli script che fanno parte dello stesso attore vengono sempre eseguiti sequenzialmente l'uno rispetto all'altro, quindi hai bisogno di più attori. Ad esempio, se metti tutti gli script di comportamento abilitati per il parallelo per il tuo NPC in un attore, essi vengono comunque eseguiti serialmente su un singolo thread, ma se hai più attori per diversi logiche NPC, ognuno di essi viene eseguito in parallelo su un proprio thread. Per ulteriori informazioni, vedi Best practices.

Codice parallelo in Attori che viene eseguito serialmente su un singolo thread
Codice parallelo in Attori che viene eseguito simultaneamente su più thread

Sicurezza dei thread

Durante l'esecuzione parallela, puoi accedere alla maggior parte delle istanze della gerarchia DataModel come al solito, ma alcune proprietà e funzioni dell'API non sono sicure da leggere o scrivere. Se le usi nel tuo codice parallelo, il motore Roblox può rilevare automaticamente e prevenire questi accessi.

I membri dell'API hanno un livello di sicurezza dei thread che indica se e come puoi usarli nel tuo codice parallelo, come indicato nella seguente tabella:

Livello di sicurezzaPer proprietàPer funzioni
Non sicuroNon può essere letto o scritto in parallelo.Non può essere chiamato in parallelo.
Leggi ParalleloPuò essere letto ma non scritto in parallelo.N/A
Locale SicuroPuò essere utilizzato all'interno dello stesso Attore; può essere letto ma non scritto da altri Actors in parallelo.Può essere chiamato all'interno dello stesso Attore; non può essere chiamato da altri Actors in parallelo.
SicuroPuò essere letto e scritto.Può essere chiamato.

Puoi trovare i tag di sicurezza dei thread per i membri dell'API nel riferimento API. Quando li utilizzi, dovresti anche considerare come le chiamate API o le modifiche delle proprietà possano interagire tra i thread paralleli. Di solito è sicuro per più attori leggere gli stessi dati di altri attori ma non modificare lo stato di altri attori.

Comunicazione tra thread

Nel contesto del multithreading, puoi comunque consentire agli script in diversi attori di comunicare tra loro per scambiare dati, coordinare compiti e sincronizzare attività. Il motore supporta i seguenti meccanismi per la comunicazione tra thread:

Puoi supportare più meccanismi per soddisfare le tue esigenze di comunicazione tra thread. Ad esempio, puoi inviare una tabella condivisa tramite l'API di Messaggistica degli Attori.

Messaging degli attori

L'API di messaging degli attori consente a uno script, sia in un contesto seriale che parallelo, di inviare dati a un attore nello stesso modello dei dati. La comunicazione tramite questa API è asincrona, in cui il mittente non blocca fino a quando il destinatario riceve il messaggio.

Quando invii messaggi utilizzando questa API, devi definire un argomento per categorizzare il messaggio. Ogni messaggio può essere inviato solo a un singolo attore, ma quell'attore può avere internamente più callback collegati a un messaggio. Solo gli script che sono discendenti di un attore possono ricevere messaggi.

L'API ha i seguenti metodi:

Il seguente esempio mostra come utilizzare Actor:SendMessage() per definire un argomento e inviare un messaggio dal lato del mittente:

Esempio di Sender di Messaggi

local Workspace = game:GetService("Workspace")
-- Invia due messaggi all'attore lavoratore con un argomento di "Saluto"
local workerActor = Workspace.WorkerActor
workerActor:SendMessage("Saluto", "Ciao Mondo!")
workerActor:SendMessage("Saluto", "Benvenuto")
print("Messaggi inviati")

Il seguente esempio mostra come utilizzare Actor:BindToMessageParallel() per legare un callback per un certo argomento in un contesto parallelo dal lato del ricevitore:

Esempio di Ricevitore di Messaggi

-- Ottieni l'attore a cui questo script è parentato
local actor = script:GetActor()
-- Lega un callback per il messaggio "Saluto"
actor:BindToMessageParallel("Saluto", function(greetingString)
print(actor.Name, "-", greetingString)
end)
print("Collegato ai messaggi")

Tabella condivisa

SharedTable è una struttura dati simile a una tabella accessibile da script che vengono eseguiti sotto più attori. È utile per situazioni che coinvolgono una grande quantità di dati e richiedono uno stato condiviso comune tra più thread. Ad esempio, quando più attori lavorano su uno stato del mondo comune che non è memorizzato nel modello dei dati.

Inviare una tabella condivisa a un altro attore non crea una copia dei dati. Invece, le tabelle condivise consentono aggiornamenti sicuri e atomici da parte di più script simultaneamente. Ogni aggiornamento a una tabella condivisa da un attore è immediatamente visibile a tutti gli attori. Le tabelle condivise possono anche essere duplicate in un processo efficiente in termini di risorse che utilizza la condivisione strutturale anziché copiare i dati sottostanti.

Comunicazione diretta del modello di dati

Puoi anche facilitare la comunicazione tra più thread direttamente utilizzando il modello di dati, in cui diversi attori possono scrivere e successivamente leggere proprietà o attributi. Tuttavia, per mantenere la sicurezza dei thread, gli script in esecuzione in parallelo in genere non possono scrivere nel modello di dati. Quindi, utilizzare direttamente il modello di dati per la comunicazione comporta restrizioni e può costringere gli script a sincronizzarsi frequentemente, il che può influire sulle prestazioni dei tuoi script.

Esempi

Convalida di raycasting lato server

Per un'esperienza di combattimento e battaglia, è necessario abilitare il raycasting per le armi dei tuoi utenti. Con il client che simula le armi per ottenere una buona latenza, il server deve confermare il colpo, il che implica eseguire raycast e un certo numero di euristiche che calcolano la velocità attesa del personaggio e osservano il comportamento passato.

Invece di utilizzare un singolo script centralizzato che si collega a un evento remoto che i client usano per comunicare le informazioni sui colpi, puoi eseguire ciascun processo di convalida del colpo sul lato server in parallelo con ogni personaggio utente che ha un evento remoto separato.

Lo script lato server che viene eseguito sotto l'Actor di quel personaggio si collega a questo evento remoto utilizzando una connessione parallela per eseguire la logica pertinente per confermare il colpo. Se la logica trova conferma di un colpo, il danno è detratto, il che implica modificare le proprietà, quindi inizialmente viene eseguita in modo seriale.


local Workspace = game:GetService("Workspace")
local tool = script.Parent.Parent
local remoteEvent = Instance.new("RemoteEvent") -- Crea un nuovo evento remoto e parentalo allo strumento
remoteEvent.Name = "RemoteMouseEvent" -- Rinominalo in modo che lo script locale possa cercarlo
remoteEvent.Parent = tool
local remoteEventConnection -- Crea un riferimento per la connessione all'evento remoto
-- Funzione che ascolta per un evento remoto
local function onRemoteMouseEvent(player: Player, clickLocation: CFrame)
-- SERIAL: Esegui codice di setup in modo seriale
local character = player.Character
-- Ignora il personaggio dell'utente durante il raycasting
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = { character }
-- PARALLEL: Esegui il raycast in parallelo
task.desynchronize()
local origin = tool.Handle.CFrame.Position
local epsilon = 0.01 -- Usato per estendere il ray leggermente poiché la posizione del clic potrebbe essere leggermente sfasata dall'oggetto
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: Il codice sottostante modifica lo stato al di fuori dell'attore
task.synchronize()
explosion.DestroyJointRadiusPercent = 0 -- Rendi l'esplosione non letale
explosion.Position = clickLocation.Position
-- Più attori potrebbero ottenere la stessa parte in un raycast e decidere di distruggerla
-- Questo è perfettamente sicuro ma risulterebbe in due esplosioni simultaneamente invece di una
-- Il seguente doppio controlla che l'esecuzione sia arrivata prima a questa parte
if hitPart.Parent then
explosion.Parent = Workspace
hitPart:Destroy() -- Distruggilo
end
end
end
end
-- Collega il segnale in modo seriale inizialmente poiché alcuni codici di setup non possono essere eseguiti in parallelo
remoteEventConnection = remoteEvent.OnServerEvent:Connect(onRemoteMouseEvent)

Generazione procedurale del terreno lato server

Per creare un vasto mondo per la tua esperienza, puoi popolare il mondo dinamicamente. La generazione procedurale crea tipicamente chunk di terreno indipendenti, con il generatore che svolge calcoli relativamente complessi per il posizionamento degli oggetti, l'uso dei materiali e il riempimento dei voxel. Eseguire il codice di generazione in parallelo può migliorare l'efficienza del processo. Il seguente campione di codice funge da esempio.


-- L'esecuzione parallela richiede l'uso di attori
-- Questo script clona se stesso; l'originale avvia il processo, mentre i cloni agiscono da lavoratori
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
-- Parent tutti gli attori all'interno di se stessi
for _, actor in workers do
actor.Parent = script
end
-- Informa gli attori di generare terreno inviando messaggi
-- In questo esempio, gli attori sono scelti casualmente
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)
-- Esci dallo script originale; il resto del codice viene eseguito in ciascun attore
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
-- Lega il callback per essere chiamato nel contesto di esecuzione parallela
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)
-- Attualmente, WriteVoxels() deve essere chiamato nella fase seriale
task.synchronize()
Workspace.Terrain:WriteVoxels(
Region3.new(corner, corner + Vector3.new(16, 16, 16)),
4,
voxels.materials,
voxels.occupancy
)
end)

Migliori pratiche

Per applicare i massimi benefici della programmazione parallela, fai riferimento alle seguenti migliori pratiche quando aggiungi il tuo codice Luau:

  • Evita Calcoli Lunghi — Anche in parallelo, calcoli lunghi possono bloccare l'esecuzione di altri script e causare ritardi. Evita di utilizzare la programmazione parallela per gestire un grande volume di calcoli lunghi e non yieldable.

    Diagramma che dimostra come il sovraccarico della fase di esecuzione parallela può ancora causare ritardi
  • Usa il Giusto Numero di Attori — Per le migliori prestazioni, utilizza più Actors. Anche se il dispositivo ha meno core di Actors, la granularità consente un bilanciamento del carico più efficiente tra i core.

    Dimostrazione di come utilizzare più attori bilanci il carico tra i core

    Questo non significa che dovresti usare quanti più Actors possibile. Dovresti ancora suddividere il codice in Actors basandoti su unità logiche piuttosto che interrompere il codice con logiche connesse in diversi Actors. Ad esempio, se desideri abilitare la convalida di raycasting in parallelo, è ragionevole utilizzare 64 Actors o più invece di solo 4, anche se stai targetizzando sistemi a 4 core. Questo è prezioso per la scalabilità del sistema e consente di distribuire il lavoro in base alle capacità dell'hardware sottostante. Tuttavia, non dovresti utilizzare neanche troppi Actors, che sono difficili da mantenere.

© 2026 Roblox Corporation. Roblox, il logo Roblox e Powering Imagination sono tra i nostri marchi registrati e non registrati negli Stati Uniti. e altri paesi.