安全系统复制数据的过程的缺点是会将作业的结果隔离到每个副本中。为了克服此限制,需要将结果存储在一种名为 NativeContainer 的共享内存中。
A NativeContainer
is a managed value type that provides a safe C# wrapper for native memory. It contains a pointer to an unmanaged allocation. When used with the Unity C# Job System, a NativeContainer
allows a job to access data shared with the main thread rather than working with a copy.
Unity 附带了一个名为 NativeArray 的 NativeContainer
。您也可以使用 NativeSlice 操作 NativeArray
,从特定位置获取特定长度的 NativeArray
子集。
Note: The Entity Component System (ECS) package extends the Unity.Collections
namespace to include other types of NativeContainer
:
NativeList
- 可调整大小的 NativeArray
。NativeHashMap
- 键/值对。NativeMultiHashMap
- 每个键有多个值。NativeQueue
- 先进先出 (FIFO) 队列。安全系统内置于所有 NativeContainer
类型中。此系统会跟踪在 NativeContainer
中读写的内容。
Note: All safety checks on NativeContainer
types (such as out of bounds checks, deallocation checks, and race condition checks) are only available in the Unity Editor and Play mode.
Part of this safety system is the DisposeSentinel and AtomicSafetyHandle. The DisposeSentinel
detects memory leaks and gives you an error if you haven’t freed your memory correctly. Triggering the memory leak error happens long after the leak occurred.
使用 AtomicSafetyHandle
可以在代码中转移 NativeContainer
的所有权。例如,如果两个调度的作业正在写入相同的 NativeArray
,则安全系统会抛出一个异常,并显示一条明确的错误消息,说明问题的原因和解决方法。调度违规的作业时,安全系统会抛出此异常。
在这种情况下,可以调度具有依赖关系的作业。第一个作业可以写入 NativeContainer
,一旦该作业完成执行,下一个作业就可以安全地读取和写入相同的 NativeContainer
。从主线程访问数据时,读写限制也适用。安全系统允许多个作业并行读取相同的数据。
By default, when a job has access to a NativeContainer
, it has both read and write access. This configuration can slow performance. The C# Job System doesn’t allow you to schedule a job that has write access to a NativeContainer
at the same time as another job that’s writing to it.
If a job doesn’t need to write to a NativeContainer
, mark the NativeContainer
with the [ReadOnly]
attribute, like so:
[ReadOnly]
public NativeArray<int> input;
在上面的示例中,允许多个作业同时对 NativeArray
进行只读访问。
注意:无法防止从作业中访问静态数据。访问静态数据时会绕过所有安全系统,并可能导致 Unity 崩溃。如需了解更多信息,请参阅 C# 作业系统提示和故障排除。
When creating a NativeContainer
, you must specify the memory allocation type that you need. The allocation type depends on the length of time the job runs. This way you can tailor the allocation to get the best performance possible in each situation.
There are three Allocator types for NativeContainer
memory allocation and release. You must specify the appropriate one when instantiating a NativeContainer
.
Temp
to pass NativeContainer
allocations to jobs.Temp
but is faster than Persistent
. Use it for thread-safe allocations within a lifespan of four frames. Important: You must Dispose
of this type of allocation within four frames, or the console prints a warning, generated from the native code. Most small jobs use this NativeContainer
allocation type.NativeContainer
allocation type. Don’t use Persistent
where performance is essential.例如:
NativeArray<float> result = new NativeArray<float>(1, Allocator.TempJob);
Note: The number 1 in the example above indicates the size of the NativeArray
. In this case, it has only one array element because it only stores one piece of data in result
.