Was experimenting with parallelism to optimize some things in my game.
I have the following structure:
In the Bananator script (please do not take these names seriously), I was trying to access the following function in ProjectileHandler:
function heartbeatConnection(bullet: Projectile): RBXScriptConnection
return RunService.Heartbeat:ConnectParallel(function(deltaTime)
local instant = workspace:GetServerTimeNow()
for i = bullet._lastThrotle, instant - THROTLE, THROTLE do
local newPosition = throttle(bullet, THROTLE)
bullet._lastThrotle = i
if not bullet.instance then continue end
assert(bullet.instance.PrimaryPart, "Bullet "..bullet._species.." does not have PrimaryPart!")
bullet.instance.PrimaryPart.Position = newPosition
end
end)
end
With this said, I constantly have the error “Event RunService.Heartbeat is not safe to access in parallel”, although I found no evidence that Heartbeat was unsafe in parallel, within the documentation.
I also tried creating a connection in parallel with this same event in the Bananator script, and that worked, without throwing that same error.
Is there a way to fix this? Thank you, in advance.
Not all Roblox services, especially shared resources like RunService.Heartbeat, are thread-safe or designed for parallel execution.
If you want to handle projectile updates with parallelism, you can’t use RunService.Heartbeat directly in parallel threads. You can try to delegate computations to parallel functions that don’t directly manipulate the Heartbeat event. Move all Roblox-specific API calls (like updating positions or accessing instances) to the sequential section.
Example Solution
-- Main sequential thread that handles updates
function heartbeatConnection(bullet: Projectile): RBXScriptConnection
return RunService.Heartbeat:Connect(function(deltaTime)
-- Do the parallel computation first
task.synchronize(function()
local instant = workspace:GetServerTimeNow()
-- Your parallel task (without direct Heartbeat calls)
task.desynchronize()
for i = bullet._lastThrottle, instant - THROTTLE, THROTTLE do
local newPosition = throttle(bullet, THROTTLE)
bullet._lastThrottle = i
-- Back to synchronized part for instance manipulations
task.synchronize()
if not bullet.instance then continue end
assert(bullet.instance.PrimaryPart, "Bullet "..bullet._species.." does not exist!")
bullet.instance.PrimaryPart.Position = newPosition
end
end)
end)
end
To be clear; RunService.Heartbeat:ConnectParallel() is doable but what matters is the type of operations that happen within the parallelized function.
Parallel-safe tasks: Operations such as mathematical calculations or processing data can be done in parallel.
Not parallel-safe tasks: Anything that modifies game objects (such as setting the position of a Part or calling functions on instances) needs to be synchronized.
Before modifying anything related to Roblox instances (e.g., positions, properties), you need to call task.synchronize() to bring the code back to the main thread, as modifying instances in parallel will result in thread safety violations.
you cannot connect to events during parallel
you can only call Connect or ConnectParallel in serial execution
so, do this:
function heartbeatConnection(bullet: Projectile): RBXScriptConnection
task.synchronize() -- return to serial execution before connecting
return RunService.Heartbeat:ConnectParallel(function(deltaTime)
local instant = workspace:GetServerTimeNow()
task.synchronize() -- writing to instance properties are not allowed in parallel, so return to serial here as well
for i = bullet._lastThrotle, instant - THROTLE, THROTLE do
local newPosition = throttle(bullet, THROTLE)
bullet._lastThrotle = i
if not bullet.instance then continue end
assert(bullet.instance.PrimaryPart, "Bullet "..bullet._species.." does not have PrimaryPart!")
bullet.instance.PrimaryPart.Position = newPosition
end
end)
end
btw, if the documentation doesn’t give a tag about thread safety for something, it means that it’s unsafe (cannot use in parallel)
if it says Read Parallel, you can only edit the property/call the function in serial
if it says Write Parallel, you can absolutely use it in parallel