上篇提到NativeContainer托管的数据类型,为Unity线程和job线程进行数据交换,它的内存分配有三种分配类型,当初始化时需要指定一个合适的分配器。有:
1.Allocator.Temp是最快的分配类型。它适用于分配一个生命周期只有一帧或更短时间的操作。你不应当把一个分配器为Temp类型分配的NativeContainer传递给jobs使用。你同时需要在函数返回之前调用Dispose方法(例如MonoBehaviour.Update,或者其他从原生到托管代码的掉用)。
2.Allocator.TempJob是相比于Temp一个较慢的分配类型,但它比Persistent要快。如果你在四帧内没有掉用Dispose,控制台会打印一个由原生代码生成的警告信息。绝大部分小jobs使用这种类型的NativeContainer分配器。
3.Allocator.Persistent是最慢的分配类型,但它可以持续存在到你需要的时间,如果必要的话可以贯穿应用程序的整个生命周期。它是直接调用malloc的一个封装。长时间的job可以使用这种分配类型。当性能比较紧张的时候你不应当使用Persistent。
IJob 作业系统的其中一个接口,其次还要知道:
1.JOB是一个一个的开线程任务,因为数据是顺序执行的所以它可以保证正确性。
2.如果想让线程人物真正的并行,那么可以采用IJobParallelFor。
3.一旦线程任务并行的话,意味着数据的执行顺序不是线性的,每一个Job里的数据不能完全依赖上一个Job执行后的结果。
4.[ReadOnly]声明数据只是制度,如果数据是只读的,意味着这个数据不需要枷锁。
5.如果不声明默认数据是Read/WriteDE的,数据一旦需要改写,那么Job就一定要等它。
6.然而这一切Unity都已经帮我们做好了,我们并不需要自己做枷锁解锁逻辑。
案例五:IJob多线程控制指定数量cube向上移动(Scne6)
脚本:IJobTest
using UnityEngine;
using Unity.Jobs;
using Unity.Collections;
public class IJobTest : MonoBehaviour
{
struct VelocityJob : IJob
{
public NativeArray<Vector3> positions;
public NativeArray<Vector3> velocitys;
public float delaTime;
public void Execute()
{
for (int i = 0; i < positions.Length; i++)
{
positions[i] = positions[i] + velocitys[i] * delaTime;
}
}
}
public int gameCount = 300;
public GameObject prefab;
public GameObject[] gameObjs;
void Start()
{
gameObjs = new GameObject[gameCount];
for (int i = 0; i < gameCount; i++)
{
gameObjs[i] = Instantiate<GameObject>(prefab);
gameObjs[i].transform.position = UnityEngine.Random.insideUnitSphere*40;
}
}
void Update()
{
// 1.准备数据
NativeArray<Vector3> tmpPositions = new NativeArray<Vector3>(gameCount, Allocator.TempJob);
NativeArray<Vector3> tmpVelocitys = new NativeArray<Vector3>(gameCount, Allocator.TempJob);
for (int i = 0; i < gameCount; i++)
{
tmpVelocitys[i] = new Vector3(0, 1, 0);
//tmpPositions[i] = tmpPositions[i] + tmpVelocitys[i] * Time.deltaTime;
tmpPositions[i] = gameObjs[i].transform.position;
}
VelocityJob job = new VelocityJob()
{
positions = tmpPositions,
delaTime = Time.deltaTime,
velocitys = tmpVelocitys
};
// 2.执行
//信号量 主线程如何知道子线程执行完毕
JobHandle jobHandle = job.Schedule();
// 3.同步
jobHandle.Complete();
for (int i = 0; i < gameCount; i++)
{
gameObjs[i].transform.position = tmpPositions[i];
}
tmpPositions.Dispose();
tmpVelocitys.Dispose();
}
}
如上图,通过预制体,动态生成了300个cube,而cube向上移动的数据,是通过多线程进行计算的。
远程项目仓库(github):https://github.com/h1031464180/UnityECS