英文原文:
https://docs.unity3d.com/Manual/JobSystemNativeContainer.html
安全系统复制数据过程的缺点是,它还隔离了每个副本中的Job结果。要克服这一限制,您需要将结果存储在一种名为NativeContainer的共享内存中。
什么是NativeContainer?
NativeContainer是一种托管值类型,它为本机内存提供了相对安全的C#包装。它包含指向非托管分配的指针。当与Unity C# JobSystem一起使用时,NativeContainer允许Job访问与主线程共享的数据,而不是使用副本。
有哪些类型的NativeContainer可用?
Unity附带一个名为NativeArray的NativeContainer。还可以使用NativeSlice操作NativeArray,以获取从特定位置到特定长度的NativeArray的子集。
注意:实体组件系统(ECS)包扩展了Unity.Collects命名空间,以包括其他类型的NativeContainer:
- NativeList - 可调整大小的NativeArray。
- NativeHashMap - 键和值对。
- NativeMultiHashMap - 每个键多个值。
- NativeQueue-先进先出(FIFO)队列。
NativeContainer和安全系统
安全系统内置于所有NativeContainer类型中。它跟踪正在读取和写入任何NativeContainer的内容。
注意:NativeContainer类型的所有安全检查(如越界检查、释放检查和竞争条件检查)仅在Unity Editor和Play模式下可用。
这个安全系统的一部分是DisposeSentinel和AtomicSafetyHandle。DisposeSentinel会检测内存泄漏,如果您没有正确释放内存,它会给您一个错误。触发内存泄漏错误发生在泄漏发生很久之后。
使用AtomicSafetyHandle在代码中转移NativeContainer的所有权。例如,如果两个scheduled jobs写入同一个NativeArray,则安全系统会抛出异常,并显示一条清晰的错误消息,解释问题的原因和解决方法。当您安排违规作业时,安全系统会抛出此异常。
在这种情况下,您可以计划具有依赖项的Job。第一个Job可以写入NativeContainer,一旦完成执行,下一个Job就可以安全地读写同一个NativeContainer。当从主线程访问数据时,读写限制也适用。安全系统确实允许多个Job并行读取相同的数据。
默认情况下,当Job有权访问NativeContainer时,它同时具有读写访问权限。此配置可能会降低性能。C# Job系统不允许您调度对NativeContainer具有写访问权限的Job,同时调度另一个正在写入它的Job。
如果作业不需要写入NativeContainer,请使用[ReadOnly]属性标记NativeContainer,如下所示:
[ReadOnly]
public NativeArray<int> input;
在上面的示例中,您可以与对第一个NativeArray也具有只读访问权限的其他Job同时执行该Job。
注意:没有针对从Job内访问静态数据的保护。访问静态数据会绕过所有安全系统,可能会使Unity崩溃。有关更多信息,请参见C#作业系统提示和故障排除。
NativeContainer分配器
创建NativeContainer时,必须指定所需的内存分配类型。分配类型取决于Job运行的时间长度。这样,您就可以调整分配,以便在每种情况下都能获得最佳性能。
NativeContainer内存分配和释放有三种分配器类型。实例化NativeContainer时,需要指定适当的参数。
-
Allocator.Temp 的分配速度最快。它适用于寿命为一帧或更短的分配。不应使用TEMP将NativeContainer分配传递给Job。在从方法调用(如MonoBehaviour.Update或从本机代码到托管代码的任何其他回调)返回之前,还需要调用Dispose方法。
-
Allocator.TempJob的分配速度比Temp慢,但比Persistent快。它用于在四个帧的生命周期内进行分配,并且是线程安全的。如果您没有在四个帧内处理它,控制台将打印一个从本机代码生成的警告。大多数小型Job都使用此NativeContainer分配类型。
-
Allocator.Persistent是最慢的分配,但只要您需要,它可以持续多久,如果需要,也可以持续整个应用程序的生命周期。它是对malloc的直接调用的包装器。较长的Job可以使用此NativeContainer分配类型。在性能至关重要的情况下,不应使用持久性。
示例:
NativeArray<float> result = new NativeArray<float>(1, Allocator.TempJob);
注意:上例中的数字1表示NativeArray的大小。在本例中,它只有一个数组元素(因为它在Result中只存储一段数据)。