Version: 2021.3
C# 作业系统中的安全系统
创建作业

NativeContainer

安全系统复制数据的过程的缺点是会将作业的结果隔离到每个副本中。为了克服此限制,需要将结果存储在一种名为 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.

Types of NativeContainer

Unity 附带了一个名为 NativeArrayNativeContainer。您也可以使用 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 类型中。此系统会跟踪在 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# 作业系统提示和故障排除

NativeContainer Allocator

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.

  • Allocator.Temp has the fastest allocation. Use it for allocations with a lifespan of one frame or fewer. However, you can’t use Temp to pass NativeContainer allocations to jobs.
  • Allocator.TempJob is a slower allocation than 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.
  • Allocator.Persistent is the slowest allocation but can last as long as you need it to, and if necessary, throughout the application’s lifetime. It is a wrapper for a direct call to malloc. Longer jobs can use this 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.

C# 作业系统中的安全系统
创建作业