Native 容器 是值类型,这意味着当它们被赋值给变量时,Unity 会复制 NativeContainer
结构,其中包含指向 Native 容器数据存储位置的指针,包括其 AtomicSafetyHandle
。它不会复制 NativeContainer
的全部内容。
这种情况意味着可能存在多个 NativeContainer
结构的副本,它们都引用相同的内存区域,并且都包含引用相同中央记录的 AtomicSafetyHandle
对象。
上面的图表显示了三个不同的 NativeArray
结构副本,它们都代表同一个实际容器。每个副本都指向同一个存储数据,以及与原始 NativeArray
相同的安全数据。但是,每个 NativeArray
副本都有不同的标志,指示作业可以使用该副本进行哪些操作。指向安全数据的指针与标志一起构成了 AtomicSafetyHandle
。
如果一个 NativeContainer
被释放,所有 NativeContainer
结构的副本都必须识别出原始 NativeContainer
无效。释放原始 NativeContainer
意味着用于保存 NativeContainer
数据的内存块已被释放。在这种情况下,存储在每个 NativeContainer
副本中的数据指针无效,如果使用它可能会导致访问冲突。
AtomicSafetyHandle
也指向一个中央记录,该记录对于 NativeContainer
实例而言无效。但是,安全系统永远不会释放中央记录的内存,因此避免了访问冲突的风险。
相反,每个记录都包含一个版本号。版本号的副本存储在每个引用该记录的 AtomicSafetyHandle
中。当一个 NativeContainer 被释放时,Unity 会调用 Release()
,这会增加中央记录上的版本号。此后,该记录可以重复用于其他 NativeContainer
实例。
每个剩余的 AtomicSafetyHandle
会将其存储的版本号与其在中央记录中的版本号进行比较,以测试 NativeContainer
是否已被释放。Unity 在调用 CheckReadAndThrow
和 CheckWriteAndThrow
等方法时会自动执行此测试。
动态 Native 容器是指具有可变大小的容器,您可以继续向其中添加元素,例如 NativeList<T>
(在 Collections 包中可用)。这与静态 Native 容器(如 NativeArray<T>
)形成对比,静态 Native 容器具有固定的大小,您无法更改。
当您使用动态 Native 容器时,您也可以通过另一个接口直接访问其数据,称为视图。视图允许您为 NativeContainer
对象的数据创建别名,而无需复制或获取数据的所有权。视图的示例包括枚举器对象,您可以使用这些对象逐元素访问 Native 容器中的数据,以及诸如 NativeList<T>.AsArray
之类的 方法,您可以使用这些方法将 NativeList
视为 NativeArray
。
如果动态 Native 容器的大小发生变化,视图通常不是线程安全的。这是因为当 Native 容器的大小发生变化时,Unity 会重新分配数据在内存中的存储位置,这会导致视图存储的任何指针都变得无效。
为了支持动态 Native 容器大小发生变化的情况,安全系统在 AtomicSafetyHandle
中包含一个辅助版本号。该机制类似于版本控制机制,但使用存储在中央记录中的第二个版本号,可以独立于第一个版本号进行递增。
要使用辅助版本号,您可以使用 UseSecondaryVersion
将视图配置到存储在 NativeContainer
中的数据中。对于更改 Native 容器大小的操作,或者使现有视图无效的操作,请使用 CheckWriteAndBumpSecondaryVersion
而不是 CheckWriteAndThrow
。您还需要在 NativeContainer
上设置 SetBumpSecondaryVersionOnScheduleWrite
,以便在计划写入 Native 容器的作业时自动使视图无效。
当您创建视图并将 AtomicSafetyHandle
复制到视图时,请使用 CheckGetSecondaryDataPointerAndThrow
确认将指针复制到 Native 容器的内存到视图中是安全的。
在处理临时 Native 容器时,您可以使用两种特殊句柄
GetTempMemoryHandle
:返回一个 AtomicSafetyHandle
,您可以将其用于使用 Allocator.Temp
分配的 Native 容器。当当前临时内存范围退出时,Unity 会自动使该句柄无效,因此您无需自己释放它。要测试特定 AtomicSafetyHandle
是否是 GetTempMemoryHandle
返回的句柄,请使用 IsTempMemoryHandle
。GetTempUnsafePtrSliceHandle
:返回一个全局句柄,您可以将其用于由不安全内存支持的临时 Native 容器。例如,从堆栈内存构造的 NativeSlice
。您不能将使用此句柄的容器传递给作业。