Avec le modèlisationde programmation Parallèle Luau , vous pouvez exécuter du code sur plusieurs threads simultanément, ce qui peut améliorer les performances de votre expérience.Au fur et à mesure que vous élargissez votre expérience avec plus de contenu, vous pouvez adopter ce modèle pour aider à maintenir les performances et la sécurité de vos scripts Luau.
modèlisationde programmation parallèle
Par défaut, les scripts s'exécutent séquentiellement.Si votre expérience a une logique ou un contenu complexe, comme des personnages non joueurs (PNJ), la validation du lancer de rayons et la génération procédurale, l'exécution séquentielle peut causer un décalage pour vos utilisateurs.Avec le modèlisationde programmation parallèle, vous pouvez diviser les tâches en plusieurs scripts et les exécuter en parallèle.Cela fait en sorte que le code de votre expérience s'exécute plus rapidement, ce qui améliore l'expérience utilisateur.
Le modèle de programmation parallèle ajoute également des avantages de sécurité à votre code.En divisant le code en plusieurs colonnes, lorsque vous éditez du code dans une fil, cela n'affecte pas d'autres codes exécutés en parallèle.Cela réduit le risque d'avoir un bug dans votre code qui corrompt toute l'expérience et minimise le délai pour les utilisateurs sur les serveurs en direct lorsque vous poussez une mise à jour.
L'adoption du modèle de programmation parallèle ne signifie pas de mettre tout dans plusieurs fils.Par exemple, la validation du lancer de rayons côté serveur définit chaque utilisateur individuel un événement distant en parallèle mais nécessite toujours que le code initial s'exécute en série pour modifier les propriétés globales, ce qui est un modèle commun pour l'exécution parallèle.
La plupart du temps, vous devez combiner des phases série et parallèle pour obtenir le sortiesouhaité, puisque actuellement, certaines opérations non prises en charge en parallèle peuvent empêcher l'exécution de scripts, telles que la modification d'instances en phases parallèles.Pour plus d'informations sur le niveau d'utilisation des API en parallèle, voir sécurité des threads.
Diviser le code en plusieurs colonnes
Pour exécuter les scripts de votre expérience dans plusieurs colonnes simultanément, vous devez les diviser en morceaux logiques sous différents acteurs dans le modèle de données .Les acteurs sont représentés par Actor instances héritant de DataModel .Ils fonctionnent comme des unités d'isolation d'exécution qui distribuent la charge sur plusieurs cœurs fonctionnant simultanément.
Placer des instances d'acteur
Vous pouvez mettre des acteurs dans des conteneurs appropriés ou les utiliser pour remplacer les types d'instance de niveau supérieur de vos entités 3D telles que les PNJ et les lanceurs de rayons, puis ajouter les scripts correspondants.

Pour la plupart des situations, vous ne devriez pas mettre un acteur comme enfant d'un autre acteur dans le modèlisationde données.Cependant, si vous décidez de placer un script imbriqué dans plusieurs acteurs pour votre cas d'utilisation spécifique, le script appartient à son acteur ancestral le plus proche.

Désynchroniser les threads
Bien que le fait de placer des scripts sous des acteurs leur donne la capacité d'exécution parallèle, par défaut, le code s'exécute toujours sur une seule colonne en série, ce qui n'améliore pas les performances d'exécution en temps réel.Vous devez appeler le task.desynchronize(), une fonction disponible qui suspend l'exécution de la coroutine actuelle pour exécuter du code en parallèle et le reprend à la prochaine occasion d'exécution parallèle.Pour retourner un script à l'exécution en série, appelez task.synchronize() .
Alternativement, vous pouvez utiliser la méthode RBXScriptSignal:ConnectParallel() lorsque vous souhaitez programmer un rappel de signal pour exécuter immédiatement votre code en parallèle lors du déclenchement.Vous n'avez pas besoin d'appeler task.desynchronize() à l'intérieur du rappel du signal.
Désynchroniser une colonne de threads
local RunService = game:GetService("RunService")
RunService.Heartbeat:ConnectParallel(function()
... -- Du code parallèle qui calcule une mise à jour d'état
task.synchronize()
... -- Du code série qui change l'état des instances
end)
Les scripts faisant partie du même acteur s'exécutent toujours séquentiellement les uns par rapport aux autres, vous avez donc besoin de plusieurs acteurs.Par exemple, si vous mettez tous les scripts de comportement activables en parallèle pour votre PNJ dans un acteur, ils s'exécutent toujours en série sur un seul fil, mais si vous avez plusieurs acteurs pour différentes logiques de PNJ, chacun d'entre eux s'exécute en parallèle sur son propre fil.Pour plus d'informations, voir Meilleures pratiques.


Sécurité des colonnes
Pendant l'exécution parallèle, vous pouvez accéder à la plupart des instances de la hiérarchie DataModel comme d'habitude, mais certaines propriétés et fonctions de l'API ne sont pas sûres à lire ou à écrire.Si vous les utilisez dans votre code parallèle, le moteur Roblox peut automatiquement détecter et empêcher ces accès de se produire.
Les membres de l'API ont un niveau de sécurité de la colonne qui indique si et comment vous pouvez les utiliser dans votre code parallèle, comme le montre le tableau suivant :
niveaude sécurité | Pour les propriétés | Pour les fonctions |
---|---|---|
Insécurité | Ne peut pas être lu ou écrit en parallèle. | Ne peut pas être appelé en parallèle. |
Lire en parallèle | Peut être lu mais pas écrit en parallèle. | N/A |
Coffre-fort local | Peut être utilisé dans le même acteur ; peut être lu mais pas écrit par d'autres Actors en parallèle. | Peut être appelé dans le même acteur ; ne peut pas être appelé par d'autres Actors en parallèle. |
Sûr | Peut être lu et écrit. | Peut être appelé. |
Vous pouvez trouver des balises de sécurité des colonnes pour les membres de l'API sur la référence de l'API.Lors de leur utilisation, vous devez également tenir compte de la manière dont les appels d'API ou les modifications de propriété peuvent interagir entre les threads parallèles.D'habitude, il est sûr que plusieurs acteurs lisent les mêmes données que d'autres acteurs, mais ne modifient pas l'état d'autres acteurs.
Communication entre colonnes croisées
Dans le contexte multicolonnes, vous pouvez toujours permettre aux scripts de différents acteurs de communiquer entre eux pour échanger des données, coordonner des tâches et synchroniser des activités.Le moteur prend en charge les mécanismes suivants pour la communication entre colonnes :
- Messagerie d'acteur API pour envoyer des messages à un acteur à l'aide de scripts.
- Structure de données de table partagée pour partager efficacement une grande quantité de données entre plusieurs acteurs sur un état partagé.
- Communication directe du modèle de données pour une communication simple avec des restrictions.
Vous pouvez prendre en charge plusieurs mécanismes pour répondre à vos besoins de communication entre colonnes.Par exemple, vous pouvez envoyer une table partagée via l'API de messagerie d'acteur.
Messagerie d'acteur
L'API de messagerie acteur permet à un script, soit dans un contexte série ou parallèle, d'envoyer des données à un acteur dans le même modèlisationdonnées.La communication via cette API est asynchrone, dans laquelle l'expéditeur ne bloque pas jusqu'à ce que le destinataire reçoive le message.
Lors de l'envoi de messages à l'aide de cette API, vous devez définir un sujet pour catégoriser le message .Chaque message ne peut être envoyé qu'à un seul acteur, mais cet acteur peut avoir plusieurs rappels internes liés à un message.Seuls les scripts qui sont des descendants d'un acteur peuvent recevoir des messages.
L'API a les méthodes suivantes :
- Actor:SendMessage() pour envoyer un message à un acteur.
- Actor:BindToMessage() pour lier un rappel Luau à un message avec le sujet spécifié dans un contexte série.
- Actor:BindToMessageParallel() pour lier un rappel Luau à un message avec le sujet spécifié dans un contexte parallèle.
L'exemple suivant montre comment utiliser Actor:SendMessage() pour définir un sujet et envoyer un message du terminerde l'expéditeur :
Expéditeur de message d'exemple
local Workspace = game:GetService("Workspace")-- Envoyez deux messages à l'acteur travailleur avec un sujet de « Salutation »local workerActor = Workspace.WorkerActorworkerActor:SendMessage("Greeting", "Hello World!")workerActor:SendMessage("Greeting", "Welcome")print("Sent messages")
L'exemple suivant montre comment utiliser Actor:BindToMessageParallel() pour lier un rappel pour un certain sujet dans un contexte parallèle du terminerdu récepteur :
Récepteur de message d'exemple
-- Obtenez l'acteur auquel ce script est associé
local actor = script:GetActor()
-- Lier un rappel pour le sujet de message « Salutation »
actor:BindToMessageParallel("Greeting", function(greetingString)
print(actor.Name, "-", greetingString)
end)
print("Bound to messages")
Table partagée
SharedTable est une structure de données semblable à une table accessible à partir de scripts fonctionnant sous plusieurs acteurs.Il est utile pour les situations qui impliquent une grande quantité de données et nécessitent un état partagé commun entre plusieurs threads.Par exemple, lorsque plusieurs acteurs travaillent sur un état mondial commun qui n'est pas stocké dans le modèlisationde données.
L'envoi d'une table partagée à un autre acteur ne fait pas une copie des données.Au lieu de cela, les tables partagées permettent des mises à jour sûres et atomiques par plusieurs scripts simultanément.Chaque mise à jour d'une table partagée par un acteur est immédiatement visible pour tous les acteurs.Les tables partagées peuvent également être clonées dans un processus économe en ressources qui utilise le partage structurel au lieu de copier les données sous-jacentes.
Communication directe du modèle de données
Vous pouvez également faciliter la communication entre plusieurs threads directement à l'aide du modèlisationde données, dans lequel différents acteurs peuvent écrire et lire ensuite des propriétés ou des attributs.Cependant, pour maintenir la sécurité des threads, les scripts s'exécutant en parallèle ne peuvent généralement pas écrire dans le modèlisationde données.Donc, l'utilisation directe du modèle de données pour la communication est soumise à des restrictions et peut forcer les scripts à se synchroniser fréquemment, ce qui peut affecter les performances de vos scripts.
Exemples
Validation du lancer de rayons côté serveur
Pour une expérience de combat et de bataille, vous devez activer le lancer de rayons pour les armes de vos utilisateurs.Avec le client qui simule les armes pour obtenir une bonne latence, le serveur doit confirmer le coup, ce qui implique de faire des raycasts et une certaine quantité d'heuristiques qui calculent la vitesse de caractère attendue et regardent le comportement passé.
Au lieu d'utiliser un seul script centralisé qui se connecte à un événement distant que les clients utilisent pour communiquer les informations de hit, vous pouvez exécuter chaque processus de validation de hit du côté du serveur en parallèle avec chaque personnage d'utilisateur ayant un événement distant séparé.
Le script côté serveur qui s'exécute sous l'événement distant de ce personnage Actor se connecte à cet événement distant en utilisant une connexion parallèle pour exécuter la logique pertinente pour confirmer le coup.Si la logique trouve une confirmation d'un coup, les dommages sont déduits, ce qui implique de modifier les propriétés, de sorte qu'ils s'exécutent en série initialement.
local Workspace = game:GetService("Workspace")
local tool = script.Parent.Parent
local remoteEvent = Instance.new("RemoteEvent") -- Créer un nouvel événement distant et le parent à l'outil
remoteEvent.Name = "RemoteMouseEvent" -- Renommez-le pour que le script local puisse le rechercher
remoteEvent.Parent = tool
local remoteEventConnection -- Créer une référence pour la connexion d'événement à distance
-- Fonction qui écoute pour un événement distant
local function onRemoteMouseEvent(player: Player, clickLocation: CFrame)
-- SERIAL : Exécuter le code d'installation en série
local character = player.Character
-- Ignorer le caractère de l'utilisateur lors du lancer de rayons
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = { character }
-- PARALLÈLE : Effectuer le lancer de rayon en parallèle
task.desynchronize()
local origin = tool.Handle.CFrame.Position
local epsilon = 0.01 -- Utilisé pour étendre légèrement le rayon puisque l'emplacement du clic pourrait être légèrement décalé par rapport à l'objet
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 : Le code ci-dessous modifie l'état en dehors de l'acteur
task.synchronize()
explosion.DestroyJointRadiusPercent = 0 -- Rendre l'explosion non mortelle
explosion.Position = clickLocation.Position
-- Plusieurs acteurs pourraient obtenir la même partie dans un raycast et décider de la détruire
-- C'est parfaitement sûr, mais cela entraînerait deux explosions à la fois au lieu d'une
-- Les vérifications suivantes garantissent que l'exécution arrive d'abord à cette partie
if hitPart.Parent then
explosion.Parent = Workspace
hitPart:Destroy() -- Détruisez-le
end
end
end
end
-- Connectez le signal en série initialement puisque certains codes d'initialisation ne peuvent pas être exécutés en parallèle
remoteEventConnection = remoteEvent.OnServerEvent:Connect(onRemoteMouseEvent)
Génération de terrain procédurale côté serveur
Pour créer un vaste monde pour votre expérience, vous pouvez remplir le monde dynamiquement.La génération procédurale crée généralement des morceaux de terrain indépendants, le générateur effectuant des calculs relativement complexes pour le placement d'objets, l'utilisation de matériaux et le remplissage de voxels.L'exécution du code de génération en parallèle peut améliorer l'efficience du processus.L'exemple de code suivant sert d'exemple.
-- L'exécution parallèle nécessite l'utilisation d'acteurs
-- Ce script se clone lui-même ; l'original initie le processus, tandis que les clones agissent en tant que travailleurs
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 tous les acteurs sous soi-même
for _, actor in workers do
actor.Parent = script
end
-- Installez les acteurs pour générer du terrain en envoyant des messages
-- Dans cet exemple, les acteurs sont choisis au hasard
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)
-- Sortie du script original ; le reste du code s'exécute dans chaque acteur
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
-- Lier le rappel à être appelé dans le contexte d'exécution parallèle
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)
-- Actuellement, WriteVoxels() doit être appelé dans la phase série
task.synchronize()
Workspace.Terrain:WriteVoxels(
Region3.new(corner, corner + Vector3.new(16, 16, 16)),
4,
voxels.materials,
voxels.occupancy
)
end)
Meilleures pratiques
Pour appliquer les avantages maximum de la programmation parallèle, référez-vous aux meilleures pratiques suivantes lors de l'ajout de votre code Luau :
Évitez les longs calculs — Même en parallèle, les longs calculs peuvent bloquer l'exécution d'autres scripts et causer un décalage.Évitez d'utiliser la programmation parallèle pour gérer un grand volume de calculs longs et inflexibles.
Utiliser le bon nombre d'acteurs — Pour obtenir les meilleures performances, utilisez plus Actors .Même si l'appareil a moins de cœurs que Actors, la granularité permet un équilibrage de charge plus efficace entre les cœurs.
Cela ne signifie pas que vous devez utiliser autant de Actors que possible.Vous devriez toujours diviser le code en Actors en fonction des unités logiques plutôt que de casser le code avec une logique connectée à différentes Actors .Par exemple, si vous voulez activer la validation du lancer de rayons en parallèle, il est raisonnable d'utiliser 64 et plus au lieu de seulement 4, même si vous ciblez des systèmes à 4 cœurs.Cela est précieux pour la scalabilité du système et lui permet de distribuer le travail en fonction de la capacité du matériel sous-jacent.Cependant, vous ne devriez pas non plus utiliser trop de Actors , qui sont difficiles à maintenir.