# 复制 NativeContainer 结构

> Copy and reference multiple native containers.

[原生容器](/engine/6000.0/manual/scripting/optimization/job-system/native-container.md)是值的类型，这意味着当将原生容器分配给变量时，Unity 会复制 `NativeContainer` 结构，而结构中包含了指向原生容器数据（包括其 [`AtomicSafetyHandle`](/engine/6000.0/script-reference/unity-collections-low-level-unsafe/atomic-safety-handle.md)）存储位置的指针。这并不会复制 `NativeContainer` 的全部内容。

这种场景意味着可能存在多个 `NativeContainer` 结构的副本，且这些副本可能都会引用同一内存区域，且都包含引用同一中央记录的 `AtomicSafetyHandle` 对象。


**NativeContainer 对象副本的工作原理:**
![](/api/media?file=/engine/6000.0/media/images/native-container-diagram.png)

上图显示了 `NativeArray` 结构的三个不同副本，它们都表示同一个实际容器。每个副本都指向与原始 `NativeArray` 相同的存储数据和相同的安全数据。但是，每个 `NativeArray` 的副本的标志都不同，分别指示了作业可对该副本执行的操作。指向安全数据的指针与标志组合构成了 `AtomicSafetyHandle`。

## 版本号

如果 `NativeContainer` 被弃置，则 `NativeContainer` 结构的所有副本都必须将原始 `NativeContainer` 视为无效。弃置原始 `NativeContainer` 意味着用于保存 `NativeContainer` 数据的内存块已被释放。这时，指向存储在各 `NativeContainer` 副本中的数据指针是无效的，强行使用可能导致访问违规。

`AtomicSafetyHandle` 还指向对 `NativeContainer` 实例无效的中心记录。但安全系统永远不会为中央记录释放内存，从而避免了访问违规的风险。

相反，所有记录都包含一个版本号。所有引用该记录的 `AtomicSafetyHandle` 中都存储了版本号的副本。弃置 NativeContainer 时，Unity 会调用 `Release()`，从而让中央记录的版本号递增。此后，记录可被用于其他 `NativeContainer` 实例。

所有剩余的 `AtomicSafetyHandle `会将其存储的版本号与中央记录中的版本号进行比较，以测试 `NativeContainer` 是否已弃置。Unity 在调用 [`CheckReadAndThrow`](/engine/6000.0/script-reference/unity-collections-low-level-unsafe/atomic-safety-handle/check-read-and-throw.md) 和 [`CheckWriteAndThrow`](/engine/6000.0/script-reference/unity-collections-low-level-unsafe/atomic-safety-handle/check-write-and-throw.md) 等方法时会自动执行此测试。

## 动态原生容器的静态视图

动态原生容器的大小可变，允许持续向其中添加元素，例如 [`NativeList<T>`](https://docs.unity3d.com/Packages/com.unity.collections@latest/index.html?subfolder=/api/Unity.Collections.NativeList-1.html)（由 Collections 包提供）。这与 [`NativeArray<T>`](/engine/6000.0/script-reference/unity-collections/native-array-1.md) 等静态原生容器相反，静态原生容器大小固定，无法更改。

使用动态原生容器时，还可以通过另一种接口（即视图）直接访问其数据。视图让您可以对 `NativeContainer` 对象数据设置别名，且无需复制或拥有数据。视图包括枚举器对象（可用于对原生容器元素中的数据进行逐数据访问）以及 [`NativeList<T>.AsArray`](https://docs.unity3d.com/Packages/com.unity.collections@latest/index.html?subfolder=/api/Unity.Collections.NativeList-1.html#Unity_Collections_NativeList_1_AsArray) 等方法（可用于将 `NativeList` 视为 `NativeArray` 进行处理）。

如果动态原生容器的大小发生变化，则视图通常不具备线程安全性。这是因为，当原生容器的大小发生变化时，Unity 会重新定位数据存储在内存中的位置，从而导致视图所存储的指针无效。

### 二级版本号

为支持动态原生容器大小发生变化的情况，安全系统会在 `AtomicSafetyHandle` 中包含二级版本号。此机制与版本控制机制类似，但使用存储在中央记录中的第二个版本号，而该版本号可以独立于第一个版本号进行递增。

要使用二级版本号，可以使用 [`UseSecondaryVersion`](/engine/6000.0/script-reference/unity-collections-low-level-unsafe/atomic-safety-handle/use-secondary-version.md) 将视图配置为存储在 `NativeContainer` 中的数据。对于更改原生容器大小的操作，或导致现有视图无效的操作，请使用 [`CheckWriteAndBumpSecondaryVersion`](/engine/6000.0/script-reference/unity-collections-low-level-unsafe/atomic-safety-handle/check-write-and-bump-secondary-version.md) 而非 `CheckWriteAndThrow`。还需要为 `NativeContainer` 设置 [`SetBumpSecondaryVersionOnScheduleWrite`](/engine/6000.0/script-reference/unity-collections-low-level-unsafe/atomic-safety-handle/set-bump-secondary-version-on-schedule-write.md)，以便在调度写入原生容器的作业时自动使视图无效。

当您创建视图并将 `AtomicSafetyHandle` 复制到视图时，请使用 [`CheckGetSecondaryDataPointerAndThrow`](/engine/6000.0/script-reference/unity-collections-low-level-unsafe/atomic-safety-handle/check-get-secondary-data-pointer-and-throw.md)，以确保将指向原生容器内存的指针复制到视图中的安全性。

## 特殊句柄

使用临时原生容器时，可用的特殊句柄有二：

* [`GetTempMemoryHandle`](/engine/6000.0/script-reference/unity-collections-low-level-unsafe/atomic-safety-handle/get-temp-memory-handle.md)：返回可在通过 `Allocator.Temp` 分配的原生容器中使用的 `AtomicSafetyHandle`。当前临时内存范围退出时，Unity 会自动使此句柄无效，因此你无需亲自释放。要测试特定的 `AtomicSafetyHandle` 是否为 `GetTempMemoryHandle` 返回的句柄，请使用 `IsTempMemoryHandle`。
* [`GetTempUnsafePtrSliceHandle`](/engine/6000.0/script-reference/unity-collections-low-level-unsafe/atomic-safety-handle/get-temp-unsafe-ptr-slice-handle.md)：返回一个全局句柄，可用于由不安全内存支持的临时原生容器。例如，由堆栈内存构造的 `NativeSlice`。无法将使用此句柄的容器传递给作业。

## 其他资源

* [实现自定义 NativeContainer](/engine/6000.0/manual/scripting/optimization/job-system/native-container/custom-nativecontainer.md)
* [自定义 NativeContainer 示例](/engine/6000.0/manual/scripting/optimization/job-system/native-container/custom-nativecontainer-example.md)
