线程安全类型
Understand Unity's custom thread-safe type, NativeContainer.
阅读时间7 分钟最后更新于 1 个月前
作业系统与 Burst 编译器搭配使用时效果最佳。由于 Burst 不支持托管对象,因此您需要使用非托管类型来访问作业中的数据。为此,您可以使用 blittable 类型,或使用 Unity 内置的 对象,这些对象对于本机内存而言是线程安全的 C# 封装器。 对象也允许作业访问与主线程共享的数据,而不是使用副本。
NativeContainerNativeContainerNativeContainers 的类型
命名空间 包含以下内置的 对象:
Unity.CollectionsNativeContainer- :非托管数组,会将原生内存缓冲区公开给托管代码。
NativeArray - :从特定位置获取到特定长度的
NativeSlice子集。NativeArray
读写权限
默认情况下,如果某项作业能够访问 ,那么该作业便具有读写权限。这种配置可能会降低性能。这是因为,当一项作业对 实例进行写入时,作业系统不允许您再调度另一项拥有写入权限的作业。
NativeContainerNativeContainer但如果作业不需要对 实例进行写入,那么您可以使用 属性标记该 ,如下所示:
NativeContainer[ReadOnly]NativeContainer[ReadOnly]public NativeArray<int> input;
在上方示例中,您可以在多个作业对首个 拥有只读权限的同时执行该作业。
NativeArray内存分配
创建 实例时,必须指定所需的内存分配类型。使用的分配类型取决于您需要将原生容器保留多长时间的可用性。这样一来,您可以调整分配方案,从而确保在各种情况下都能获得最佳性能。
NativeContainer - :最快的分配。适用于生命周期为一帧或更短的分配。您不能使用
Allocator.Temp将分配传递给存储在作业成员字段中的Temp实例。NativeContainer - :分配速度比
Allocator.TempJob慢,但比Temp快。适用于生命周期为四帧的线程安全分配。重要信息:您必须在四帧内对这种分配执行Persistent,否则控制台会打印一条由原生代码生成的警告。大多数小型作业都使用这种分配类型。Dispose - :最慢的分配,但可以按需持续存在,如有必要,甚至可以在应用程序的整个生命周期内存在。它是对
Allocator.Persistent的直接调用的封装器。持续时间较长的作业可以使用这种 NativeContainer 分配类型。当性能至关重要时,不要使用malloc。Persistent
例如:
NativeArray<float> result = new NativeArray<float>(1, Allocator.TempJob);
**注意:**上例中的数字 1 表示 NativeArray 的大小。此例子中只有一个数组元素,因为在 result 中只存储了一段数据。
NativeContainer 安全系统
所有 实例中都内置了安全系统。安全系统会跟踪任何对 实例的读取或写入,并使用这些信息对 的使用强制执行某些规则,使其在多个作业和线程中以确定的方式运行。
NativeContainerNativeContainerNativeContainer例如,如果两个独立的受调度作业对同一 进行写入,则不安全,因为无法预测哪个作业会被首先执行。这意味着您将不知道哪项作业会覆盖另一项作业的数据。在调度第二项作业时,安全系统会抛出一个异常,并显示一条明确的错误消息,说明问题的原因和解决方法。
NativeArray如果要调度两项对同一 实例进行写入的作业,可以调度具有依赖项的作业。第一项作业会对 进行项,执行完毕后,下一项作业就可以安全地对同一 进行读取和写入。引入依赖关系可确保作业始终以一致的顺序执行,并确保 中生成的数据具有确定性。
NativeContainerNativeContainerNativeContainerNativeContainer安全系统允许多项作业并行读取相同的数据。
这些读写限制同样适用于从主线程访问数据的情况。例如,如果您尝试在某项对 NativeContainer 进行写入的作业完成前读取 NativeContainer 的内容,则安全系统会抛出错误。同样,如果您在仍有对 NativeContainer 进行读取或写入的待处理作业时尝试对 NativeContainer 进行写入,那么安全系统也会抛出错误。
此外,由于 NativeContainers 不实现 ,您无法直接更改 的内容。例如, 等同于写入 ,后者不会更新 中的值。
ref returnNativeContainernativeArray[0]++;var temp = nativeArray[0]; temp++;nativeArray正确的做法是,必须将索引中的数据复制到本地临时副本中,修改该副本,然后将其存回。例如:
MyStruct temp = myNativeArray[i];temp.memberVariable = 0;myNativeArray[i] = temp;