Version: 2020.3
ParallelForTransform 作业
多玩家和联网

C# 作业系统提示和故障排除

使用 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 方法

只能从主线程调用 ScheduleComplete。如果一个作业依赖于另一个作业,请使用 JobHandle 来管理依赖关系,而不是尝试在作业中调度作业。

在适当时间使用 Schedule 和 Complete 方法

拥有作业所需的数据后就立即在作业上调用 Schedule,并仅在需要结果时才开始在作业上调用 Complete。最好是调度当前不与正在运行的任何其他作业竞争的、不需要等待的作业。例如,如果在一帧结束和下一帧开始之间的一段时间没有作业正在运行,并且可以接受一帧延迟,则可以在一帧结束时调度作业,并在下一帧中使用其结果。另一方面,如果游戏占满了与其他作业的转换期,但在帧中的其他位置存在大量未充分利用的时段,那么在这个时段调度作业会更加有效。

将 NativeContainer 类型标记为只读

请注意,默认情况下,作业对 NativeContainer 类型具有读写访问权限。请在适当时使用 [ReadOnly] 属性来提高性能。

检查数据依赖关系

在 Unity Profiler 窗口中,主线程上的标记“WaitForJobGroup”表示 Unity 正在等待工作线程上的作业完成。此标记可能意味着您已在某处引入了应处理的数据依赖关系。请查找 JobHandle.Complete 来跟踪在何处存在强制主线程等待的数据依赖关系。

调试作业

作业具有一个 Run 函数,可以使用此函数代替 Schedule 立即在主线程上执行作业。可将此函数用于调试。

不要在作业中分配托管内存

在作业中分配托管内存非常慢,而且作业无法使用 Unity Burst 编译器来提高性能。Burst 是一种新的基于 LLVM 的后端编译器技术,可以简化您的工作。此编译器获取 C# 作业并生成高度优化的机器代码,从而利用目标平台的特定功能。

其他信息


  • 2018–06–15 页面已发布

  • 2018.1 版中公开了 C# 作业系统 NewIn20181

ParallelForTransform 作业
多玩家和联网