文档

Unity Engine

Unity Engine

复制 NativeContainer 结构

Copy and reference multiple native containers.
阅读时间6 分钟最后更新于 1 个月前

原生容器是值的类型,这意味着当将原生容器分配给变量时,Unity 会复制
NativeContainer
结构,而结构中包含了指向原生容器数据(包括其
AtomicSafetyHandle
)存储位置的指针。这并不会复制
NativeContainer
的全部内容。
这种场景意味着可能存在多个
NativeContainer
结构的副本,且这些副本可能都会引用同一内存区域,且都包含引用同一中央记录的
AtomicSafetyHandle
对象。

NativeContainer 对象副本的工作原理

上图显示了
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
    。无法将使用此句柄的容器传递给作业。

其他资源