浅析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<int> input;

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

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用UnityJob System来构建Mesh可以提高游戏的性能,因为它可以在多个CPU核心上同时处理数据。 首先,你需要为你的Mesh数据创建一个结构体,以便Job System可以处理它。例如,你可以创建一个包含所有Mesh数据的结构体,如下所示: ``` struct MeshData { public Vector3[] vertices; public int[] triangles; ... } ``` 接下来,你需要创建一个Job,用于生成Mesh。你可以使用IJobParallelFor或IJobForEach来处理每个顶点或三角形。例如,如果要处理每个顶点,可以编写以下代码: ``` struct GenerateMeshJob : IJobParallelFor { public MeshData meshData; public void Execute(int i) { // Generate vertex data meshData.vertices[i] = ... } } ``` 然后,你需要在主线程中分配和初始化MeshData结构体,并将其传递给Job: ``` var meshData = new MeshData(); // Initialize meshData vertices, triangles, etc. var job = new GenerateMeshJob() { meshData = meshData }; var jobHandle = job.Schedule(meshData.vertices.Length, 64); ``` 最后,你需要等待Job完成,并将数据写入Mesh: ``` jobHandle.Complete(); var mesh = new Mesh(); mesh.vertices = meshData.vertices; mesh.triangles = meshData.triangles; ... ``` 需要注意的是,使用Job System构建Mesh需要一定的编程经验和技巧,因为它涉及到多线程编程和数据同步等问题。因此,如果你是新手开发者,建议先熟悉Job System的基本用法和原理,然后再尝试使用它来构建Mesh。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值