Quick answer: Chain dependent jobs via JobHandle so writes are serialized. If a NativeArray is only read in one job, mark it [ReadOnly]. For random-index writes, use [NativeDisableParallelForRestriction] only when index uniqueness is proven.
Your Burst job system schedules two jobs to process the same NativeArray. Unity throws: “Job… has not completed yet. It writes the same array as Job…”. The warning is correct — you’d have a data race — but isn’t obvious how to fix.
Why the Safety System Blocks This
Unity’s job system tracks read/write access to each NativeContainer. Two parallel jobs with write access to the same container would race. The safety system refuses to schedule the second job until the first completes, and prints a warning to make this visible.
Fix 1: Chain via Dependencies
var handle1 = job1.Schedule(positions.Length, 64);
var handle2 = job2.Schedule(positions.Length, 64, handle1);
handle2.Complete();
Passing handle1 as the second argument tells the scheduler “don’t start job2 until job1 finishes”. Writes are serialized. Performance: job1 and job2 run sequentially, but main thread continues during both.
Fix 2: ReadOnly Where Possible
[BurstCompile]
public struct AnalyzeJob : IJobParallelFor
{
[ReadOnly] public NativeArray<float3> positions;
public NativeArray<float> distances;
public void Execute(int index)
{
distances[index] = math.length(positions[index]);
}
}
The [ReadOnly] attribute on positions tells the safety system: this job only reads. Another job can read positions in parallel without conflict.
Fix 3: NativeDisableParallelForRestriction
[BurstCompile]
public struct ScatterJob : IJobParallelFor
{
[ReadOnly] public NativeArray<int> targetIndices;
[NativeDisableParallelForRestriction] public NativeArray<float> results;
public void Execute(int index)
{
int target = targetIndices[index];
results[target] = math.sqrt(index); // random index write
}
}
This attribute disables the safety check for that container. Use only when you can guarantee that no two parallel iterations write to the same index — otherwise undefined behavior.
Fix 4: NativeQueue / NativeStream for Producers
For genuinely parallel writes to a shared destination, use a parallel-safe container:
[BurstCompile]
public struct ProduceJob : IJobParallelFor
{
[WriteOnly] public NativeQueue<Event>.ParallelWriter queue;
public void Execute(int index) {
queue.Enqueue(new Event(index));
}
}
NativeQueue’s ParallelWriter is designed for concurrent writes. Order isn’t guaranteed (queue ends up unordered) but the operations are race-free.
Diagnosing Excessive Completes
If your fix is “call CompleteAll”, the perf gain from Jobs evaporates because the main thread blocks. Look for opportunities to chain via JobHandle dependencies and to defer Complete to end-of-frame.
Verifying
Run with the Jobs Debugger open. The schedule should show no conflicts. Profiling should reveal parallel execution where independent, sequential where dependencies exist. No warnings in Player.log.
“The Jobs safety system is your friend. Don’t suppress; fix the access pattern.”
Add [ReadOnly] reflexively to any NativeArray you only read — eliminates entire classes of warnings.