JobSystem 在旧版本中仅供 Unity 内部 C++ native 层使用。自 Unity 2018 起,该功能向 managed 层(C#)开放,配合 Burst 编译器,开发者可以在 C# 层更轻松地编写多线程代码。
官方指引文档:https://docs.unity3d.com/Manual/JobSystem.html
JobSystem 的核心设计思路:
- 通过拷贝和只读等手段避免数据竞争。
- 引入 NativeContainer,提供多线程数据交互的解决方案。
A ParallelFor job dividing batches across cores
JobSystem 注意事项
详见官方文档:https://docs.unity3d.com/Manual/JobSystemTroubleshooting.html
- Do not access static data from a job
- Flush scheduled batches — 由于调度是在主线程发起的,如果在循环中大量调用 Schedule,已被调度的 Job 需等待循环结束才能执行。若主线程存在大量计算,反而会增加延迟。可使用
JobHandle.ScheduleBatchedJobs立即触发执行,无需等到 Complete 调用。 - Don't try to update NativeContainer contents
- Call JobHandle.Complete to regain ownership
- Use Schedule and Complete in the main thread
- Use Schedule and Complete at the right time
- Mark NativeContainer types as read-only
- Do not allocate managed memory in jobs
JobSystem 性能提升示例
开启 JobSystem 后,帧率稳定在 60 FPS 左右(CPU 使用率 68%);关闭后帧率降至 20 FPS 左右(CPU 使用率 13%)。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Jobs;
using Unity.Jobs.LowLevel.Unsafe;
using Unity.Collections;
using Unity.Transforms;
using UnityEngine.Jobs;
public struct Job3 : IJobParallelForTransform
{
// public NativeArray<int> result;//ref
// public Job3()//NativeArray<int> result)
// {
// this.result = result;
// }
public void Execute(int index, TransformAccess transform)
{
//calc operation
{
var rot = Quaternion.LookRotation(Vector3.forward);
for (int i = 0; i < 256; i++)
{
rot = Quaternion.LookRotation(Vector3.forward);
}
transform.rotation = Quaternion.LookRotation(Vector3.forward);
transform.position = Vector3.one;
}
// Debug.LogError(System.Threading.Thread.CurrentThread.ManagedThreadId + " " + index);
}
}
public class Test1 : MonoBehaviour
{
public List<GameObject> _list = new List<GameObject>();
public GameObject prototype;
TransformAccessArray result;
void t3()
{
//use job system
var j1 = new Job3();
var handle = j1.Schedule(result);
//wait for job done
handle.Complete();
//do in main thread
/* foreach (var p in _list)
{
p.transform.position += Vector3.one;
p.transform.rotation = Quaternion.LookRotation(Vector3.forward);
var rot = Quaternion.LookRotation(Vector3.forward);
for (int i = 0; i < 256; i++)
{
rot = Quaternion.LookRotation(Vector3.forward);
}
}*/
}
void Start()
{
const int len = 1024;
TransformAccessArray.Allocate(len, len, out result);
for (int i = 0; i < len; i++)
{
_list.Add(GameObject.Instantiate<GameObject>(prototype));
result.Add(_list[i].transform);
}
}
void Update()
{
t3();
}
}