浅析Unity的C# Job System

一、 C# Job System
多线程使程序更高效,但是想要实现多线程需同时避免频繁的上下文切换、线程的资源的竞争

What is a job system?
通过创建一个Jobs代替线程来管理多线程代码
将jobs 放入job queue中执行,job system中的工作线程从job queue中提取并执行它们
job system管理依赖项并确保jobs以适当的顺序执行。

What is a job?
job是完成一项特定任务的小工作单位。job接收参数并对数据进行操作,类似于方法调用的行为。job可以是独立的,也可以在运行之前依赖于其他jobs来完成。

What are job dependencies?
在复杂的系统中,比如游戏开发所需要的系统,不太可能每个工作都是独立的。一个job通常是为下一份job准备数据。job意识到并支持依赖关系使其工作。如果jobA依赖于jobB,作业系统将确保jobA在jobB完成之前不会开始执行。

Race Condition
在编写多线程代码时,总是存在竞争条件的风险。当一个操作的输出依赖于其控制之外的另一个进程的时间时,就会出现竞态条件。
竞态条件并不总是错误,但它是不确定行为的来源。当竞态条件确实导致bug时,可能很难找到问题的根源,因为这取决于时间,所以只能在很少的情况下重新创建问题。调试它会导致问题消失,因为断点和日志记录会改变单个线程的时间。竞态条件在编写多线程代码时产生了最大的挑战。

Safety system
为了更容易编写多线程代码,Unity c# Job system检测所有潜在的竞态条件,并保护您不受它们可能导致的bug的影响。
例如:如果c# Job system将数据的引用从主线程中的代码发送到Job,它无法验证主线程是否在Job写入数据的同时读取数据。这个情况就创建了一个竞态条件。
c# Job system通过向每个作业发送它需要操作的数据的副本来解决这个问题,而不是向主线程中的数据发送引用。此副本隔离数据,从而消除竞态条件。
c# Job system复制数据的方式意味着Job只能访问可交换数据类型。当在托管代码和本机代码之间传递时,这些类型不需要转换。
c#作业系统可以使用memcpy复制可blittable类型,并在Unity的托管部分和本机部分之间传输数据。它在调度作业时使用memcpy将数据放入本机内存,并在执行作业时让托管端访问该副本。

NativeContainer
安全系统复制数据过程的缺点是,它还将作业的结果隔离在每个副本中。为了克服这个限制,您需要将结果存储在一种名为NativeContainer的共享内存类型中。

What is a NativeContainer?
NativeContainer是一种托管值类型,它为本机内存提供了一个相对安全的c#包装器。它包含指向非托管分配的指针。当与Unity c# Job System一起使用时,NativeContainer允许Job访问与主线程共享的数据,而不是使用副本。

What types of NativeContainer are available?
Unity附带一个名为NativeArray的本地保存器。您还可以使用NativeSlice操作NativeArray,以从特定位置获得NativeArray的一个子集,以达到特定的长度。
注意:实体组件系统(ECS)包扩展了Unity集合命名空间,以包括其他类型的NativeContainer:
一个可调整大小的NativeArray。
NativeHashMap -键和值对。
NativeMultiHashMap——每个键有多个值。
NativeQueue 一个先进先出(FIFO)队列。

安全系统内置在所有本机保持器类型中。它跟踪对任何本机保留程序的读和写操作。
注意:所有关于本机保持器类型的安全检查(如越界检查、解除位置检查和竞态条件检查)仅在Unity编辑器和播放模式中可用。
这个安全系统的一部分是处理entinel和AtomicSafetyHandle。dispose sesentinel检测内存泄漏,如果没有正确释放内存,则会给出一个错误。触发内存泄漏错误发生在泄漏发生很久之后。
使用AtomicSafetyHandle在代码中转移本地保存程序的所有权。例如,如果两个计划的Job正在写入到同一个NativeArray,安全系统将抛出一个异常,其中包含一条清晰的错误消息,解释为什么以及如何解决这个问题。当您计划违规作业时,安全系统会抛出此异常。
在这种情况下,您可以使用依赖关系来调度作业。第一个Job可以写入NativeContainer,一旦它完成执行,下一个Job就可以安全地读取和写入同一个NativeContainer。读写限制也适用于从主线程访问数据。安全系统允许多个Job并行读取同一数据。
默认情况下,当作业访问NativeContainer时,它同时具有读和写访问。这种配置可能会降低性能。c#作业系统不允许您调度对本机保留程序具有写访问权的作业,同时调度对本机保留程序具有写访问权的Job。
如果作业不需要写入NativeContainer,则用[ReadOnly]属性标记NativeContainer,如下所示:

[ReadOnly]
public NativeArray input;

在上面的例子中,您可以与对第一个NativeArray具有只读访问权的其他作业同时执行作业。
注意:没有针对从作业中访问静态数据的保护。访问静态数据会绕过所有安全系统,破坏统一。

你可能感兴趣的:(Unity)