Unity JobSystem 使用教程
深入讲解 Unity C# JobSystem 的核心概念、常见 Job 类型、依赖管理、Burst 编译器加速及性能调优,助你多核释放潜能。

适用版本:Unity 2022.3 LTS+,示例项目启用 Entities 1.2 & Burst 1.8。旧版 JobSystem API 亦可参考。

目录

  1. 前言
  2. 为何要用 JobSystem
  3. 核心概念速览
  4. 创建第一个 Job
  5. 常用 Job 接口
  6. 依赖与调度
  7. Burst 编译器
  8. Native 容器与内存安全
  9. 调试与 Profiler
  10. 案例:并行地形高度计算
  11. 常见坑与最佳实践
  12. 结语

前言

在现代移动与主机硬件上,核心数稳步提升,而主线程瓶颈成为性能硬伤。Unity C# JobSystem 通过易用的 API,把密集计算拆分到工作线程,实现 真·多核利用。本文将带你从入门到进阶,掌握 Job 编写、调度与优化。


为何要用 JobSystem

  • 并行计算:充分利用 CPU 多核。
  • 主线程减负:将耗时逻辑移出 Update(),提升帧率。
  • 数据导向:与 ECS(Entities)无缝协作,享受线性内存布局。

JobSystem ≠ 传统 C# Thread,它由 Unity 调度器管理线程池,更安全高效。


核心概念速览

名称 作用
IJob 单任务,适合少量计算
IJobParallelFor 数据分片并行
IJobChunk 与 ECS Chunk 同步
JobHandle 调度后返回,管理依赖
Schedule() 异步执行
ScheduleParallel() 分片并行
Complete() 阻塞主线程等待执行完

创建第一个 Job

using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;

[BurstCompile]                // 开启 Burst 优化
public struct SqrtJob : IJob
{
    public NativeArray<float> values;

    public void Execute()
    {
        for (int i = 0; i < values.Length; i++)
            values[i] = Mathf.Sqrt(values[i]);
    }
}

public class JobExample : MonoBehaviour
{
    void Start()
    {
        var data = new NativeArray<float>(100, Allocator.TempJob);
        for (int i = 0; i < data.Length; i++) data[i] = i;

        var job = new SqrtJob { values = data };
        JobHandle handle = job.Schedule();   // 调度
        handle.Complete();                   // 等待完成

        Debug.Log($"Result[10] = {data[10]}"); // √10
        data.Dispose();
    }
}

常用 Job 接口

接口 适用场景
IJobParallelFor 大量元素独立计算(物理、AI 感知)
IJobParallelForTransform 批量更新 Transform
IJobChunk 在 ECS Chunk 粒度并行
IJobEntityBatch Entities.ForEach 的底层实现

示例 —— 并行向量归一化:

[BurstCompile]
public struct NormalizeJob : IJobParallelFor
{
    public NativeArray<float3> vectors;
    public void Execute(int index) => vectors[index] = math.normalize(vectors[index]);
}

依赖与调度

JobHandle h1 = job1.Schedule();
JobHandle h2 = job2.Schedule(h1);   // h2 依赖 h1
JobHandle h3 = JobHandle.CombineDependencies(h1, h2);
h3.Complete();                      // 等待全部完成
  • 依赖链:避免数据竞争。
  • BatchCountScheduleParallel 第 3 参数决定切片大小。

Burst 编译器

  • 向量化:生成 SIMD 指令(SSE4/NEON/AVX2)。
  • 跨平台:一次编写,多架构优化。
  • 调试Jobs → Burst → Enable Safety Checks 可查看潜在越界。
[BurstCompile(FloatPrecision.Standard, FloatMode.Fast)]

PlayerSettings→Other→ScriptingBackend 使用 IL2CPP 与 Mono 都可启用 Burst。


Native 容器与内存安全

容器 线程安全 特点
NativeArray<T> 线性存储
NativeList<T> 动态扩容
NativeQueue<T> Concurrent 结构体并行写
NativeHashMap<K,V> 并行读/写分离
  • 释放:Dispose()Dispose(handle) 延迟销毁。
  • 禁止在 Job 外访问正在执行的容器。

调试与 Profiler

  1. Profiler → Timeline → Jobs:查看 Job 排队与线程占用。
  2. Entity Debugger:ECS Job 状态。
  3. Burst Inspector:反汇编检查向量化效果。
  4. Unity 2023.2+Job Dependencies 视图。

案例:并行地形高度计算

[BurstCompile]
public struct HeightJob : IJobParallelFor
{
    [ReadOnly] public NativeArray<float3> positions;
    [WriteOnly] public NativeArray<float> heights;

    public void Execute(int index)
    {
        float3 p = positions[index];
        heights[index] = noise.cnoise(p * 0.1f);
    }
}

// 调度
var pos = new NativeArray<float3>(vertexCount, Allocator.TempJob);
var hgt = new NativeArray<float>(vertexCount, Allocator.TempJob);
var job = new HeightJob { positions = pos, heights = hgt };

JobHandle handle = job.ScheduleParallel(vertexCount, 64, default);
handle.Complete();
// 后续生成 Mesh 高度...

结果:CPU 耗时从 18 ms → 2 ms(iPhone 13)。


常见坑与最佳实践

  • 别在 Job 里分配托管内存(如 new List<T>())。
  • 避免 Complete() 过度:会阻塞主线程。使用 Dependency 合并等待点。
  • 合理切片:元素 < 100 时串行更快。
  • NativeList 并行写:需 NativeList<T>.ParallelWriter
  • Burst 与 Unity.Mathematics:优先使用 math.*,少用 System.Math

结语

掌握 JobSystem,你就掌握了移动端与主机的多核钥匙。结合 ECS 与 Burst,可让 CPU 性能腾飞 5–20 倍。把本文示例融入项目,体验丝滑帧率吧!


附件 · 示例脚本列表

  • Jobs/SqrtJob.cs
  • Jobs/NormalizeJob.cs
  • Jobs/HeightJob.cs

需要完整工程?留言即可获取 GitHub 地址。


Last modified on 2025-06-09