使用Unity C#作业系统时,请确保遵守以下内容:
不要从作业访问静态数据
从作业访问静态数据会绕过所有安全系统。如果您访问错误的数据,您可能会以意想不到的方式崩溃Unity。例如,访问MonoBehaviour可能会导致域重新加载崩溃。
注意:由于存在这种风险,Unity的未来版本将阻止使用静态分析从作业进行全局变量访问。如果您确实访问作业中的静态数据,则应该期望您的代码在Unity的未来版本中中断。
刷新预定批次
如果希望作业开始执行,则可以使用JobHandle.ScheduleBatchedJobs刷新计划批处理。请注意,调用此方法会对性能产生负面影响。不刷新批处理会延迟调度,直到主线程等待结果。在所有其他情况下,使用JobHandle.Complete来启动执行过程。
注意:在实体组件系统(ECS)中,将为您隐式刷新批处理,因此JobHandle.ScheduleBatchedJobs
不需要调用。
不要尝试更新NativeContainer内容
由于缺少ref返回,因此无法直接更改NativeContainer的内容。例如,nativeArray[0]++;
与var temp = nativeArray[0]; temp++;
不更新值的nativeArray
写入相同。
相反,您必须将索引中的数据复制到本地临时副本,修改该副本并将其保存回来,如下所示:
MyStruct temp = myNativeArray[i];
temp.memberVariable = 0;
myNativeArray[i] = temp;
致电JobHandle.Complete以重新获得所有权
跟踪数据所有权需要在主线程再次使用它们之前完成依赖项。检查JobHandle.IsCompleted是不够的。您必须调用该方法JobHandle.Complete
以重新获得NativeContainer
主线程的类型的所有权。调用Complete
还可以清除安全系统中的状态。不这样做会引入内存泄漏。如果您在每个帧中安排新作业,并且依赖于前一帧的作业,则此过程也适用。
在主线程中使用Schedule和Complete
你只能调用Schedule和Complete
主线程。如果一个作业依赖于另一个作业,则使用JobHandle
管理依赖关系而不是尝试在作业内安排作业。
在合适的时间使用计划和完成
Schedule
只要您拥有所需的数据就立即打电话给工作,并且Complete
在您需要结果之前不要打电话给它。优秀的做法是安排一个您不需要等待的工作,而不是与正在运行的任何其他工作竞争。例如,如果在一帧结束和下一帧的开始之间没有作业正在运行,并且可以接受一帧延迟,则可以将作业调度到帧的末尾并使用其结果在以下框架中。或者,如果您的游戏与其他工作的转换期间相比,并且框架中的其他位置存在大量未充分利用的时间段,则更有效地安排您的工作。
将NativeContainer类型标记为只读
请记住,作业NativeContainer
默认情况下对类型具有读写访问权限。[ReadOnly]
适当时使用该属性可提高性能。
检查数据依赖性
在Unity Profiler 窗口,主线程上的标记“WaitForJobGroup”表示Unity正在等待工作线程上的作业完成。此标记可能意味着您已在某处应引入数据依赖关系。寻找JobHandle.Complete
跟踪数据依赖关系的位置,这些数据依赖关系迫使主线程等待。
调试作业
作业具有一个Run函数,您可以使用它来代替Schedule
在主线程上立即执行作业。您可以将其用于调试目的。
不要在作业中分配托管内存
在作业中分配托管内存非常慢,并且该作业无法使用Unity Burst编译器来提高性能。Burst是一种新的基于LLVM的后端编译器技术,可以让您更轻松。它需要C#作业并利用您平台的特定功能生成高度优化的机器代码。
更多的信息
- 观看Unity GDC 2018:剪辑的C#作业系统播放列表。
- 有关C#作业系统如何与ECS相关的更多高级信息,请参阅GitHub上的ECS包文档。