Job System

news2024/11/16 11:40:48

01-C#Job System概述

官方文档

Unity C# Job System允许用户编写与Unity其余部分良好交互的多线程代码,并使编写正确的代码变得更加容易。

编写多线程代码可以提供高性能的好处。其中包括显着提高帧速率和延长移动设备的电池寿命。

C# Job System的一个重要方面是它与Unity内部使用的集成(Unity的native jobsystem)。用户编写的代码和Unity共享工作线程。这种合作避免了导致争用CPU资源的问题,并且可以创建比CPU核心更多的线程。

02-什么是多线程

官方文档

在单线程计算机系统中,一次只能进入一条指令,并且只能得出一个结果。加载和完成程序的时间取决于CPU需要完成的工作量。

多线程是一种编程,它利用CPU在多个内核上同时处理多个线程的能力,它不是一个接一个地执行任务或指令,而是同时运行的。

默认情况下,一个线程在程序的开头运行。这是“主线程”。主线程创建新线程来处理任务。这些新线程彼此并行运行,并且通常在完成后将其结果与主线程同步

如果您有一些运行很长时间的任务,这种多线程方法很有效。但是,游戏开发代码通常包含许多一次执行的小指令。如果为每个小指令创建一个线程,最终可能会有许多线程,每个线程的生命周期都很短。这可以推动CPU和操作系统处理能力的极限。

通过拥有一个线程池可以缓解线程生存期的问题。但是,即使您使用线程池,也可能同时激活大量线程。线程数多于CPU核心导致线程相互争用CPU资源,导致频繁的上下文切换。上下文切换是通过执行保存线程状态的过程,然后处理另一个线程,然后重新构建第一个线程,以便继续处理它。上下文切换是资源密集型的,因此您应尽可能避免使用它。

03-什么是Job System

官方文档

Job System通过创建Job而不是线程来管理多线程代码。

Job System跨多个核心管理一组工作线程。它通常每个逻辑CPU核心有一个工作线程,以避免上下文切换(尽管它可能为操作系统或其他专用应用程序保留一些核心)。

Job System将Job放入作业队列中用来执行。Job System中的工作线程从作业队列中获取Job并执行它们。作业系统管理依赖关系并确保作业以适当的顺序执行。

3.1 什么是Job?

Job是完成一项特定任务的一小部分工作。Job接收参数并对数据进行操作,类似于方法调用的行为方式。Job可以是独立的,也可以是依赖的(需要等其他作业完成后,然后才能运行。)

3.2 什么是Job依赖?

在复杂的系统中,如游戏开发所需的系统,每个工作都不可能是独立的。一项工作通常是为下一份工作准备数据。作业了解并支持依赖关系以使其发挥作用。如果jobA对jobB依赖,则Job System确保在完成jobA之前不会开始执行jobB

04-C#Job System中的安全系统

官方文档

4.1 竞争条件

编写多线程代码时,总是存在竞争条件的风险。当一个操作的输出取决于其控制之外的另一个过程的时间的时候,就会发生竞争条件。

竞争条件并不总是一个Bug,但它是不确定行为的来源。当竞争条件确实导致Bug时,可能很难找到问题的根源,因为它取决于时间,因此您只能在极少数情况下重新复现问题调试它可能会导致问题消失,因为断点和日志记录(Logging)可以改变单个线程的时间。竞争条件是编写多线程代码时最重大的挑战。

4.2 安全系统

为了更容易编写多线程代码,Unity C#作业系统可以检测所有潜在的竞争条件,并保护您免受可能导致的Bug的影响。

例如:如果C#Job System将主线程中代码中的数据引用发送到Job中,则无法验证主线程是否在作业写入数据的同时读取数据。这种情况就会创建竞争条件。

C#Job System复制数据的方式意味着作业只能访问blittable数据类型。在托管代码和本机代码之间传递时,这些类型不需要转换。

C#Job System可以使用memcpy复制blittable类型,并在Unity的托管和本机部分之间传输数据。它在调度Job时用memcpy将数据放入本机内存,并在执行作业时为托管端提供对该拷贝副本的访问权限。有关更多信息,请参阅计划作业。

05-NativeContainer

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

5.1 什么是NativeContainer?

NativeContainer是托管值类型,为本机内存提供相对安全的C#包装器。它包含指向非托管分配的指针。与Unity C#作业系统一起使用时,一个 NativeContainer允许Job访问与主线程共享的数据,而不是使用拷贝副本数据。

5.2 有哪些类型的NativeContainer?

Unity附带一个NativeContainer名为NativeArray的程序。您还可以使用NativeSlice操作一个NativeArray来获取NativeArray从指定位置到指定长度的子集。

注意:实体组件系统(ECS)包扩展了Unity.Collections命名空间以包括其他类型的NativeContainer:

NativeList- 可调整大小的NativeArray。

NativeHashMap - 键值对。

NativeMultiHashMap - 每个键有多个值。

NativeQueue- 先进先出(FIFO)队列。

5.3 NativeContainer和安全系统

安全系统内置于所有NativeContainer类型。它跟踪NativeContainer中正在阅读和写入的内容。

注意:所有NativeContainer类型的安全检查(例如越界检查,重新分配检查和竞争条件检查)仅在Unity Editor和Play模式下可用。

该安全系统的一部分是DisposeSentinel和AtomicSafetyHandle。该DisposeSentinel检测内存泄漏,如果你没有正确地释放你的内存,就会报错。内存泄漏发生后很久就会发生内存泄漏错误。

使用AtomicSafetyHandle转移NativeContainer代码的所有权。例如,如果两个调度Job写入相同NativeArray,则安全系统会抛出一个异常,并显示一条明确的错误消息,说明解决问题的原因和方法。当你调度违规Job时,安全系统会抛出此异常。

在这种情况下,您可以调度具有依赖关系的Job。第一个Job可以写入NativeContainer,一旦完成执行,下一个Job就可以安全地读取和写入上一个Job相同的NativeContainer。从主线程访问数据时,读写限制也适用。安全系统允许多个Job并行读取相同的数据。

默认情况下,当Job有权访问一个NativeContainer时,它具有读写访问权限。此配置可能会降低性能。C#Job System不允许您在一个job正在写入NativeContainer时同时调度另外一个对NativeContainer 有写入权限的Job。

如果作业不需要写入一个 NativeContainer,请使用[ReadOnly]属性标记NativeContainer,如下所示:

在上面的示例中,您可以与其他对第一个也具有只读访问权限的作业同时执行作业NativeArray。

注意:无法防止从作业中访问静态数据。访问静态数据会绕过所有安全系统,并可能导致Unity崩溃。有关更多信息,请参阅C#作业系统提示和故障排除。

5.4 NativeContainer分配器

当创建 NativeContainer时,必须指定所需的内存分配类型。分配类型取决于Job运行的时间长度。通过这种方式,您可以定制分配以在每种情况下获得最佳性能。

NativeContainer内存分配和释放有三种分配器类型。在实例化你的NativeContainer时候需要指定合适的一个类型。

1Allocator.Temp 分配的时候最快。它适用于寿命为一帧或更少的分配。您不应该使用Temp将NativeContainer分配传递给Jobs。您还需要在从方法(例如MonoBehaviour.Update,或从本机代码到托管代码的任何其他回调)调用返回之前调用该方法Dispose()。

2Allocator.TempJob 是一个比Temp慢的分配,但速度比Persistent快。它适用于四帧生命周期内的分配,并且是线程安全的。如果在四个帧内没有调用Dispose,则控制台会打印一个从本机代码生成的警告。大多数小型Jobs都使用这个NativeContainer分配类型。

3Allocator.Persistent 是最慢的分配,只要你需要它,就一直存在。并且如果有必要的话,可以持续整个应用程序的生命周期。它是直接调用malloc的包装器。较长的Jobs可以使用此NativeContainer分配类型。你不应该使用Persistent在性能至关重要的地方使用

例如:

注意:上例中的数字1表示NativeArray的大小。在这种情况下,它只有一个数组元素(因为它只存储一个数据result)。

06-创建Jobs

要在Unity中创建作业,您需要实现IJob接口。IJob允许您调度可以与其他正在运行的Job并行运行的单个Job。

注意:“Job”是Unity中用于实现IJob接口的任何结构的集合术语。

要创建Jobs,您需要:

●创建一个继承自IJob的结构体。

●添加Jobs使用的成员变量(blittable类型或NativeContainer类型)。

●在结构体中实现一个继承自IJob接口的Execute的方法。

当执行job时,这个Execute方法在单个核心上运行一次。

注意在设计job时,请记住它们在数据副本上运行,除非是NativeContainer。因此,从主线程中的Job访问数据的唯一方法是写入NativeContainer

示例

// Job adding two floating point values together

publicstructMyJob : IJob

{

publicfloata;

publicfloatb;

publicNativeArray<float>result;

publicvoidExecute()

{

result[0] =a+b;

}

}

07-调度Jobs

要在主线程中调度Job,您必须:

●实例化Job。

●填充Job的数据。

●调用Schedule方法。

调用Schedule将Job放入Job队列中以便在适当的时间执行。一旦调度,你就不能打断Job的运行。

注意您只能在主线程调用Schedule

调度Jobs的一个例子

//This example waits for the job to complete for illustration purposes

NativeArray<float>result=newNativeArray<float>(1, Allocator.TempJob);

// Set up the job data

MyJobjobData=newMyJob();

jobData.a=10;

jobData.b=10;

jobData.result=result;

// Schedule the job

JobHandlehandle=jobData.Schedule();

// Wait for the job to complete

handle.Complete();

// All copies of the NativeArray point to the same memory, you can access the result

// in "your" copy of the NativeArray

floataPlusB=result[0];

// Free the memory allocated by the result array

result.Dispose();

08-JobHandle和依赖关系

当您调用Job的Schedule方法时,它将返回JobHandle。您可以在代码中使用JobHandle 作为其他Job的依赖关系。如果Job取决于另一个Job的结果,您可以将第一个作业JobHandle作为参数传递给第二个作业的Schedule方法,如下所示:

JobHandlefirstJobHandle=firstJob.Schedule();

secondJob.Schedule(firstJobHandle);

8.1 结合依赖关系

如果Job有许多依赖项,则可以使用JobHandle.CombineDependencies方法合并它们。CombineDependencies允许您将它们传递给Schedule方法。

NativeArray<JobHandle>handles=newNativeArray<JobHandle>(numJobs, Allocator.TempJob);

// Populate `handles` with `JobHandles` from multiple scheduled jobs...

JobHandlejh=JobHandle.CombineDependencies(handles);

8.2 在主线程中等待Job

在主线程中使用JobHandle强迫让你的代码等待您的Job执行完毕。要做到这一点,调用JobHandle的方法 Complete。此时,您知道主线程可以安全地访问正在使用job 的NativeContainer。

注意:在调度Job时,Job不会开始执行。如果您正在等待主线程中的Job,并且您需要访问正在使用Job的NativeContainer数据,则可以调用该方法JobHandle.Complete。此方法从内存高速缓存中刷新Job并启动执行过程。调用JobHandle的Complete方法将返回NativeContainer的所有权到主线程。您需要再次调用 JobHandle 的Complete方法以便于再次从主线程安全地访问这些NativeContainer类型。也可以通过从Job的依赖中的JobHandle的Complete方法调用返回主线程上的所有权。例如,你可以调用jobA的Complete方法,或者也可以调用依靠JobA的JobB上的Complete方法。两者都会在调用Complete后在主线程上安全访问时使用jobA的NativeContainer类型

否则,如果您不需要访问数据,则需要明确刷新批处理。为此,请调用静态方法JobHandle.ScheduleBatchedJobs。请注意,调用此方法可能会对性能产生负面影响。

多个Jobs和dependencies的示例

publicstructMyJob : IJob

{

publicfloata;

publicfloatb;

publicNativeArray<float>result;

publicvoidExecute()

{

result[0] =a+b;

}

}

// Job adding one to a value

publicstructAddOneJob : IJob

{

publicNativeArray<float>result;

publicvoidExecute()

{

result[0] =result[0] +1;

}

}

主线程代码:

// Create a native array of a single float to store the result in.

//This example waits for the job to complete

NativeArray<float>result=newNativeArray<float>(1, Allocator.TempJob);

// Setup the data for job #1

MyJobjobData=newMyJob();

jobData.a=10;

jobData.b=10;

jobData.result=result;

// Schedule job #1

JobHandlefirstHandle=jobData.Schedule();

// Setup the data for job #2

AddOneJobincJobData=newAddOneJob();

incJobData.result=result;

// Schedule job #2

JobHandlesecondHandle=incJobData.Schedule(firstHandle);

// Wait for job #2 to complete

secondHandle.Complete();

// All copies of the NativeArray point to the same memory, you can access the result in "your" copy of the NativeArray

floataPlusB=result[0];

// Free the memory allocated by the result array

result.Dispose();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/399315.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

iOS开发-bugly符号表自动上传发布自动化shell

这里介绍的是通过build得到的app文件和dSYM文件来打包分发和符号表上传。 通过Archive方式打包和获得符号表的方式以后再说。 一&#xff1a;bugly工具jar包准备 bugly符号表工具下载地址&#xff1a;(下载完成后放入项目目录下&#xff0c;如不想加入git可通过gitIgnore忽略…

doPost的实际使用

目录 前言 一、doPost是什么&#xff1f; 二、使用步骤 1.doPost的请求方法 2.需要引入依赖 总结 前言 本章主要记录一下doPost的请求公用方法的使用。 一、doPost是什么&#xff1f; 它其实就是一个http的post请求方式。 二、使用步骤 1.doPost的请求方法 当我们系…

使用Endnote自定义参考文献格式

使用Endnote自定义参考文献格式 使用Endnote插入参考文献&#xff0c;若要设置期刊指定格式或自己想要的参考格式&#xff0c;使用EndNote自定义方法&#xff0c;步骤如下。 注&#xff1a;有的期刊会给出EndNote的格式文件&#xff0c;那样直接导入就行。 文章目录使用Endnot…

Python+Yolov8目标识别特征检测

Yolov8目标识别特征检测如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01;前言这篇博客针对<<Yolov8目标识别特征检测>>编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;易读。 学习与应用推荐…

毕业设计常用模块之温湿度模块DHT11模块使用

DHT11是一款可以测量温度数据和湿度数据的传感器 产品特点 暖通空调、除湿器、农业、冷链仓储、测试及检测设备、消费品、汽车、自动控制、数据记录器、气 象站、家电、湿度调节器、医疗、其他相关湿度检测控制 外形尺寸 第3管脚&#xff1a;NC 是没有用的 典型电路 通信方式…

表格中的table-layout属性讲解

表格中的table-layout属性讲解 定义和用法 tableLayout 属性用来显示表格单元格、行、列的算法规则。 table-layout有三个属性值&#xff1a;auto、fixed、inherit。 fixed&#xff1a;固定表格布局 固定表格布局与自动表格布局相比&#xff0c;允许浏览器更快地对表格进行布…

excel 一对多数据查询公式 经典用法

所谓一对多&#xff0c;就是符合某个指定条件的有多个结果&#xff0c;要把这些结果都提取出来。 下面咱们就说说一对多查询的典型用法&#xff0c;先看数据源&#xff1a; A~D列是一些员工信息&#xff0c;要根据F2单元格指定的学历&#xff0c;提取出所有“本科”的人员姓名…

“一网统管”视频融合平台EasyCVR增加播放限制功能,支持全局及自定义设置视频播放时长

EasyCVR平台可在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;实现视频资源的鉴权管理、按需调阅、全网分发、智能分析等。平台可支持多协议、多类型的设备接入&#xff0c;包括国标GB28181、RTMP、RTSP/Onvif、海康SDK、大华SDK…

网络 | 网络层讲解 | IP协议 | 分片处理与网段划分

文章目录前言IP报文格式分片处理分片对传输层的影响网段划分路由转发中的路由表前言 tcp作为传输层的典型协议&#xff0c;保证了报文传输的可靠性&#xff0c;使每份报文完整的传输。在传输层之下的网络层解决的是传输能力的问题&#xff0c;它使得数据可以发送到对方主机&am…

Nginx-http-flv-module流媒体服务器搭建+模拟推流+flv.js在前端html和Vue中播放HTTP-FLV视频流

场景 Windows上搭建Nginx RTMP服务器并使用FFmpeg实现本地视频推流&#xff1a; Windows上搭建Nginx RTMP服务器并使用FFmpeg实现本地视频推流_win nginx-rtmp最新版_霸道流氓气质的博客-CSDN博客 Vue中使用vue-video-player和videojs-flash插件实现播放rtmp视频文件流&…

类型转换(C++)

文章目录1. 为什么需要类型转换2. C语言的类型转换2.1 隐式类型转换2.2 显式类型转换2.3 特点3. C的类型转换3.1 static_cast3.2 reinterpret_cat3.3 const_cast3.4 dynamic_cast转型向下转型的安全问题3.5 explicit4. RTTI5. 常见题目1. 为什么需要类型转换 类型转换是将一个…

数据库-基础篇-8-事务

事务简介&#xff1a;事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求&#xff0c;即这些操作要么同时成功要么同时失败。 默认MySQL的事务是自动提交的&#xff0c;也就是说&#xff0c…

S3C2440移植Linux4.19.275内核以及过程中遇到的问题

目录 1 问题一&#xff1a;内核移植时MTD分区问题 2 问题二&#xff1a;uboot的MTDPARTS_DEFAULT定义的MTD分区&#xff0c;bootargs中的文件系统分区&#xff0c;内核的mtd_partition smdk_default_nand_part定义的分区&#xff0c;三者要对应起来 3 问题三&#xff1a;ubo…

kafka:linux 安装 kafka集群

kafka运行依赖于 jdk、zookeeper&#xff0c;kafka可视化工具选择kafka-eagle。所以要装的组件有&#xff1a;jdk、zookeeper、kafka、kafka-eagle一、安装jdk下载linux版本的jdk包&#xff0c;比如&#xff1a;jdk-8u192-linux-x64.tar.gz。将其复制到 /opt 目录下并解压&…

设计模式(十八)----行为型模式之策略模式

1、概述 先看下面的图片&#xff0c;我们去旅游选择出行模式有很多种&#xff0c;可以骑自行车、可以坐汽车、可以坐火车、可以坐飞机。 作为一个程序猿&#xff0c;开发需要选择一款开发工具&#xff0c;当然可以进行代码开发的工具有很多&#xff0c;可以选择Idea进行开发&a…

第十届省赛——7外卖店优先级

题目&#xff1a;“饱了么”外卖系统中维护着N 家外卖店&#xff0c;编号1~N。每家外卖店都有一个优先级&#xff0c;初始时(0 时刻) 优先级都为0。每经过1 个时间单位&#xff0c;如果外卖店没有订单&#xff0c;则优先级会减少1&#xff0c;最低减到0&#xff1b;而如果外卖店…

New Bing的详细申请步骤与实际功能测试效果展示

文章目录前言申请方式申请出错解决方案测试总结前言 微软表示&#xff0c;new bing正在使用对话式人工智能来创造一种新的浏览网络的方式。用户将能够像ChatGPT那样与Bing聊天&#xff0c;用自然语言提出问题和接受答案。 同时&#xff0c;微软宣布了其搜索引擎Bing的新版本&a…

汽车微控制器芯片F280039CPZRQ1、F280039CSPM、F280039CSPN规格参数

F280039CPZRQ1、F280039CSPM、F280039CSPN是C2000实时微控制器系列中的一款器件。C2000微控制器是可扩展、超低延迟器件&#xff0c;旨在提高电力电子设备的效率&#xff0c;包括但不限于&#xff1a;高功率密度、高开关频率&#xff0c;并支持使用 GaN和SiC技术。F280039CPZRQ…

6个常用Pycharm插件推荐,老手100%都用过

人生苦短 我用python 有些插件是下载后需要重启Pycharm才生效的 免费领源码、安装包&#xff1a;扣扣qun 903971231 PyCharm 本身已经足够优秀&#xff0c; 就算不使用插件&#xff0c; 也可以吊打市面上 90%的 Python 编辑器。 如果硬要我推荐几款实用的话&#xff0c; 那么…

Beats:在 Docker 中同时部署 Metricbeat 和 Elasticsearch

在本教程中&#xff0c;我们将部署一个 metricbeat 来监控正在运行的容器的健康状况和系统指标。 为什么需要监控&#xff0c;为什么需要 Metricbeat&#xff1f; 一个常见的问题&#xff0c;但很少有人回答。 首先&#xff0c;无论我们部署的是 docker 容器还是老式的金属箱&…