Con el modelo de programación Luau Paralelo, puedes ejecutar código en múltiples hilos simultáneamente, lo que puede mejorar el rendimiento de tu experiencia. A medida que amplías tu experiencia con más contenido, puedes adoptar este modelo para ayudar a mantener el rendimiento y la seguridad de tus scripts de Luau.
Modelo de programación paralelo
Por defecto, los scripts se ejecutan de forma secuencial. Si tu experiencia tiene una lógica o contenido complejos, como personajes no jugables (NPC), validación de raycasting y generación procedural, la ejecución secuencial podría causar retrasos para tus usuarios. Con el modelo de programación paralelo, puedes dividir tareas en múltiples scripts y ejecutarlos en paralelo. Esto hace que el código de tu experiencia se ejecute más rápido, lo que mejora la experiencia del usuario.
El modelo de programación paralelo también añade beneficios de seguridad a tu código. Al dividir el código en múltiples hilos, cuando editas el código en un hilo, no afecta a otro código que se está ejecutando en paralelo. Esto reduce el riesgo de que un error en tu código corrompa toda la experiencia y minimiza el retraso para los usuarios en servidores en vivo cuando publicas una actualización.
Adoptar el modelo de programación paralelo no significa poner todo en múltiples hilos. Por ejemplo, la validación de raycasting del lado del servidor establece para cada usuario individual un evento remoto en paralelo, pero aún requiere que el código inicial se ejecute de forma serial al cambiar propiedades globales, que es un patrón común para la ejecución paralela.
La mayoría de las veces, necesitas combinar fases seriales y paralelas para lograr los resultados deseados, ya que actualmente hay algunas operaciones que no son compatibles en paralelo que pueden impedir que los scripts se ejecuten, como modificar instancias en fases paralelas. Para más información sobre el nivel de uso de las API en paralelo, consulta seguridad de hilos.
Dividir el código en múltiples hilos
Para ejecutar los scripts de tu experiencia en múltiples hilos de manera concurrente, necesitas dividirlos en partes lógicas bajo diferentes actores en el modelo de datos. Los actores están representados por instancias de Actor que heredan de DataModel. Funcionan como unidades de aislamiento de ejecución que distribuyen la carga entre múltiples núcleos que se están ejecutando simultáneamente.
Colocar instancias de actores
Puedes colocar actores en contenedores apropiados o usarlos para reemplazar los tipos de instancias de nivel superior de tus entidades 3D, como NPCs y raycasters, y luego agregar los scripts correspondientes.

En la mayoría de las situaciones, no deberías poner un actor como hijo de otro actor en el modelo de datos. Sin embargo, si decides colocar un script anidado dentro de múltiples actores para tu caso de uso específico, el script pertenece a su actor ancestro más cercano.

Desincronizar hilos
Aunque colocar scripts bajo actores les otorga la capacidad de ejecución paralela, por defecto el código aún se ejecuta en un único hilo de forma secuencial, lo que no mejora el rendimiento en tiempo de ejecución. Necesitas llamar a task.desynchronize(), una función que se puede ceder que suspende la ejecución de la coroutine actual para ejecutar código en paralelo y lo reanuda en la próxima oportunidad de ejecución paralela. Para volver a cambiar un script a ejecución serial, llama a task.synchronize().
Alternativamente, puedes utilizar el método RBXScriptSignal:ConnectParallel() cuando desees programar un callback de señal para ejecutar inmediatamente tu código en paralelo al activarse. No necesitas llamar a task.desynchronize() dentro del callback de la señal.
Desincronizar un Hilo
local RunService = game:GetService("RunService")
RunService.Heartbeat:ConnectParallel(function()
... -- Algún código paralelo que calcula una actualización de estado
task.synchronize()
... -- Algún código serial que cambia el estado de las instancias
end)
Los scripts que son parte del mismo actor siempre se ejecutan secuencialmente respecto a los unos a otros, así que necesitas múltiples actores. Por ejemplo, si colocas todos los scripts de comportamiento habilitados para paralelo de tu NPC en un actor, aún se ejecutan de forma serial en un único hilo, pero si tienes múltiples actores para diferentes lógicas de NPC, cada uno de ellos se ejecuta en paralelo en su propio hilo. Para más información, consulta Mejores prácticas.


Seguridad de hilos
Durante la ejecución paralela, puedes acceder a la mayoría de las instancias de la jerarquía de DataModel como de costumbre, pero algunas propiedades y funciones de la API no son seguras para leer o escribir. Si las usas en tu código paralelo, el motor de Roblox puede detectar automáticamente y prevenir estos accesos.
Los miembros de la API tienen un nivel de seguridad de hilos que indica si y cómo puedes usarlos en tu código paralelo, como se muestra en la siguiente tabla:
| Nivel de seguridad | Para propiedades | Para funciones |
|---|---|---|
| Inseguro | No puede ser leído ni escrito en paralelo. | No puede ser llamado en paralelo. |
| Leer Paralelo | Puede ser leído pero no escrito en paralelo. | N/A |
| Seguro Local | Puede ser usado dentro del mismo Actor; puede ser leído pero no escrito por otros Actors en paralelo. | Puede ser llamado dentro del mismo Actor; no puede ser llamado por otros Actors en paralelo. |
| Seguro | Puede ser leído y escrito. | Puede ser llamado. |
Puedes encontrar etiquetas de seguridad de hilos para los miembros de la API en la referencia de la API. Al usarlos, también deberías considerar cómo las llamadas a la API o los cambios de propiedades podrían interactuar entre hilos paralelos. Generalmente, es seguro que múltiples actores lean los mismos datos que otros actores, pero no modifiquen el estado de otros actores.
Comunicación entre hilos
Bajo el contexto de multihilo, aún puedes permitir que scripts en diferentes actores se comuniquen entre sí para intercambiar datos, coordinar tareas y sincronizar actividades. El motor admite los siguientes mecanismos para la comunicación entre hilos:
- API de mensajería de actores para enviar mensajes a un actor usando scripts.
- Estructura de datos de tabla compartida para compartir de manera eficiente una gran cantidad de datos entre múltiples actores en un estado compartido.
- Comunicación directa del modelo de datos directa para una comunicación simple con restricciones.
Puedes soportar múltiples mecanismos para acomodar tus necesidades de comunicación entre hilos. Por ejemplo, puedes enviar una tabla compartida a través de la API de Mensajería de Actores.
Mensajería de actores
La API de mensajería de actores permite que un script, ya sea en un contexto serial o paralelo, envíe datos a un actor en el mismo modelo de datos. La comunicación a través de esta API es asíncrona, en la que el remitente no se bloquea hasta que el receptor recibe el mensaje.
Al enviar mensajes usando esta API, necesitas definir un tema para categorizar el mensaje. Cada mensaje solo puede ser enviado a un solo actor, pero ese actor puede tener internamente múltiples callbacks vinculados a un mensaje. Solo los scripts que son descendientes de un actor pueden recibir mensajes.
La API tiene los siguientes métodos:
- Actor:SendMessage() para enviar un mensaje a un actor.
- Actor:BindToMessage() para vincular un callback de Luau a un mensaje con el tema especificado en un contexto serial.
- Actor:BindToMessageParallel() para vincular un callback de Luau a un mensaje con el tema especificado en un contexto paralelo.
El siguiente ejemplo muestra cómo usar Actor:SendMessage() para definir un tema y enviar un mensaje desde el lado del remitente:
Ejemplo de Remitente de Mensajeslocal Workspace = game:GetService("Workspace")-- Enviar dos mensajes al actor trabajador con un tema de "Saludo"local workerActor = Workspace.WorkerActorworkerActor:SendMessage("Greeting", "¡Hola Mundo!")workerActor:SendMessage("Greeting", "Bienvenido")print("Mensajes enviados")
El siguiente ejemplo muestra cómo usar Actor:BindToMessageParallel() para vincular un callback para un tema determinado en un contexto paralelo en el lado receptor:
Ejemplo de Receptor de Mensajes
-- Obtener el actor al que este script está vinculado
local actor = script:GetActor()
-- Vincular un callback para el tema del mensaje "Saludo"
actor:BindToMessageParallel("Greeting", function(greetingString)
print(actor.Name, "-", greetingString)
end)
print("Vinculado a mensajes")
Tabla compartida
SharedTable es una estructura de datos similar a una tabla accesible desde scripts que se ejecutan bajo múltiples actores. Es útil para situaciones que involucran una gran cantidad de datos y requieren un estado compartido común entre múltiples hilos. Por ejemplo, cuando múltiples actores trabajan en un estado común del mundo que no está almacenado en el modelo de datos.
Enviar una tabla compartida a otro actor no hace una copia de los datos. En cambio, las tablas compartidas permiten actualizaciones seguras y atómicas por múltiples scripts simultáneamente. Cada actualización a una tabla compartida por un actor es inmediatamente visible para todos los actores. Las tablas compartidas también se pueden clonar en un proceso eficiente en recursos que utiliza el intercambio estructural en lugar de copiar los datos subyacentes.
Comunicación directa del modelo de datos
También puedes facilitar la comunicación entre múltiples hilos directamente usando el modelo de datos, en el que diferentes actores pueden escribir y posteriormente leer propiedades o atributos. Sin embargo, para mantener la seguridad de hilos, los scripts que se ejecutan en paralelo generalmente no pueden escribir en el modelo de datos. Por lo tanto, utilizar directamente el modelo de datos para la comunicación conlleva restricciones y puede obligar a los scripts a sincronizarse con frecuencia, lo que puede afectar el rendimiento de tus scripts.
Ejemplos
Validación de raycasting del lado del servidor
Para una experiencia de lucha y batalla, necesitas habilitar raycasting para las armas de tus usuarios. Con el cliente simulando las armas para lograr una buena latencia, el servidor tiene que confirmar el golpe, lo que implica hacer raycasts y cierta cantidad de heurísticas que calculan la velocidad esperada del personaje y observan el comportamiento pasado.
En lugar de usar un único script centralizado que se conecta a un evento remoto que los clientes utilizan para comunicar la información de golpe, puedes ejecutar cada proceso de validación de golpe del lado del servidor en paralelo, teniendo cada personaje usuario un evento remoto separado.
El script del lado del servidor que se ejecuta bajo el Actor de ese personaje se conecta a este evento remoto usando una conexión paralela para ejecutar la lógica relevante para confirmar el golpe. Si la lógica encuentra una confirmación de un golpe, se deduce el daño, lo que implica cambiar propiedades, así que se ejecuta de forma serial inicialmente.
local Workspace = game:GetService("Workspace")
local tool = script.Parent.Parent
local remoteEvent = Instance.new("RemoteEvent") -- Crear un nuevo evento remoto y ubicarlo en la herramienta
remoteEvent.Name = "RemoteMouseEvent" -- Renombrarlo para que el script local pueda buscarlo
remoteEvent.Parent = tool
local remoteEventConnection -- Crear una referencia para la conexión del evento remoto
-- Función que escucha un evento remoto
local function onRemoteMouseEvent(player: Player, clickLocation: CFrame)
-- SERIAL: Ejecutar código de configuración de forma serial
local character = player.Character
-- Ignorar el personaje del usuario mientras se realiza el raycasting
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = { character }
-- PARALLELO: Realizar el raycast en paralelo
task.desynchronize()
local origin = tool.Handle.CFrame.Position
local epsilon = 0.01 -- Utilizado para extender ligeramente el rayo ya que la ubicación del clic podría estar ligeramente desplazada del objeto
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: El código a continuación modifica el estado fuera del actor
task.synchronize()
explosion.DestroyJointRadiusPercent = 0 -- Hacer que la explosión no sea mortal
explosion.Position = clickLocation.Position
-- Múltiples actores podrían obtener la misma parte en un raycast y decidir destruirla
-- Esto es completamente seguro, pero resultaría en dos explosiones a la vez en lugar de una
-- La siguiente verificación doble asegura que la ejecución llegó a este punto primero
if hitPart.Parent then
explosion.Parent = Workspace
hitPart:Destroy() -- Destruirlo
end
end
end
end
-- Conectar la señal de forma serial inicialmente ya que algún código de configuración no puede ejecutarse en paralelo
remoteEventConnection = remoteEvent.OnServerEvent:Connect(onRemoteMouseEvent)
Generación de terreno procedural del lado del servidor
Para crear un vasto mundo para tu experiencia, puedes poblar el mundo de manera dinámica. La generación procedural típicamente crea trozos de terreno independientes, con el generador realizando cálculos relativamente intrincados para la colocación de objetos, uso de materiales y llenado de vóxeles. Ejecutar el código de generación en paralelo puede mejorar la eficiencia del proceso. El siguiente código sirve como ejemplo.
-- La ejecución paralela requiere el uso de actores
-- Este script se clona a sí mismo; el original inicia el proceso, mientras que los clones actúan como trabajadores
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
-- Hacer que todos los actores sean hijos de sí mismos
for _, actor in workers do
actor.Parent = script
end
-- Instruir a los actores para generar terreno enviando mensajes
-- En este ejemplo, los actores son elegidos aleatoriamente
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)
-- Salir del script original; el resto del código se ejecuta en cada actor
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
-- Vincular el callback para que se llame en el contexto de ejecución paralela
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)
-- Actualmente, WriteVoxels() debe ser llamado en la fase serial
task.synchronize()
Workspace.Terrain:WriteVoxels(
Region3.new(corner, corner + Vector3.new(16, 16, 16)),
4,
voxels.materials,
voxels.occupancy
)
end)
Mejores prácticas
Para aplicar los máximos beneficios de la programación paralela, consulta las siguientes mejores prácticas al agregar tu código de Luau:
Evita Cálculos Largos — Incluso en paralelo, los cálculos largos pueden bloquear la ejecución de otros scripts y causar retrasos. Evita usar la programación paralela para manejar un gran volumen de cálculos largos y que no ceden.

Utiliza la Cantidad Correcta de Actores — Para el mejor rendimiento, utiliza más Actors. Incluso si el dispositivo tiene menos núcleos que Actors, la granularidad permite una distribución de carga más eficiente entre los núcleos.

Esto no significa que debas usar tantos Actors como sea posible. Aún debes dividir el código en Actors basado en unidades lógicas en lugar de romper el código con lógica conectada en diferentes Actors. Por ejemplo, si deseas habilitar validación de raycasting en paralelo, es razonable usar 64 Actors y más en lugar de solo 4, incluso si estás apuntando a sistemas de 4 núcleos. Esto es valioso para la escalabilidad del sistema y permite distribuir el trabajo basado en la capacidad del hardware subyacente. Sin embargo, también no debes usar demasiados Actors, que son difíciles de mantener.