原生容器是值的类型,这意味着当将原生容器分配给变量时,Unity 会复制 NativeContainer 结构,而结构中包含了指向原生容器数据(包括其 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 等方法时会自动执行此测试。
动态原生容器的大小可变,允许持续向其中添加元素,例如 NativeList<T>(由 Collections 包提供)。这与 NativeArray<T> 等静态原生容器相反,静态原生容器大小固定,无法更改。
使用动态原生容器时,还可以通过另一种接口(即视图)直接访问其数据。视图让您可以对 NativeContainer 对象数据设置别名,且无需复制或拥有数据。视图包括枚举器对象(可用于对原生容器元素中的数据进行逐数据访问)以及 NativeList<T>.AsArray 等方法(可用于将 NativeList 视为 NativeArray 进行处理)。
如果动态原生容器的大小发生变化,则视图通常不具备线程安全性。这是因为,当原生容器的大小发生变化时,Unity 会重新定位数据存储在内存中的位置,从而导致视图所存储的指针无效。
为支持动态原生容器大小发生变化的情况,安全系统会在 AtomicSafetyHandle 中包含二级版本号。此机制与版本控制机制类似,但使用存储在中央记录中的第二个版本号,而该版本号可以独立于第一个版本号进行递增。
要使用二级版本号,可以使用 UseSecondaryVersion 将视图配置为存储在 NativeContainer 中的数据。对于更改原生容器大小的操作,或导致现有视图无效的操作,请使用 CheckWriteAndBumpSecondaryVersion 而非 CheckWriteAndThrow。还需要为 NativeContainer 设置 SetBumpSecondaryVersionOnScheduleWrite,以便在调度写入原生容器的作业时自动使视图无效。
当您创建视图并将 AtomicSafetyHandle 复制到视图时,请使用 CheckGetSecondaryDataPointerAndThrow,以确保将指向原生容器内存的指针复制到视图中的安全性。
使用临时原生容器时,可用的特殊句柄有二:
GetTempMemoryHandle:返回可在通过 Allocator.Temp 分配的原生容器中使用的 AtomicSafetyHandle。当前临时内存范围退出时,Unity 会自动使此句柄无效,因此你无需亲自释放。要测试特定的 AtomicSafetyHandle 是否为 GetTempMemoryHandle 返回的句柄,请使用 IsTempMemoryHandle。GetTempUnsafePtrSliceHandle:返回一个全局句柄,可用于由不安全内存支持的临时原生容器。例如,由堆栈内存构造的 NativeSlice。无法将使用此句柄的容器传递给作业。