Unity 的作业系统允许您创建多线程代码,从而让应用程序可以使用所有可用的 CPU 核心来执行代码。这样可以提高性能,因为应用程序可以更高效地利用所有参与运行的 CPU 核心,而不是用单个 CPU 核心运行所有代码。
您可以单独使用作业系统,但为了提高性能,您还应该使用 Burst 编译器,该编译器专为 Unity 的作业系统编译作业而设计。Burst 编译器改进了代码生成,从而提高了性能,并减少了移动设备的电池消耗。
您还可以让作业系统搭配 Unity 的实体组件系统 使用,从而创建高性能的面向数据的代码。
Unity 会使用自己的原生作业系统在多个工作线程上处理自己的原生代码,线程数决于运行应用程序的设备上可用的 CPU 核心数量。Unity 通常会在程序启动时所默认运行的线程上执行代码,该线程即主线程。但使用作业系统时,Unity 会在多个工作线程上执行代码,这种行为即多线程。
多线程利用了 CPU 通过多个核心同时处理多个线程的能力。此时,任务或指令不是被逐个执行的,而是同时运行的。工作线程会并行运行,并会在完成后将各自的结果与主线程同步。
作业系统可确保只有足够的线程与 CPU 核心容量相匹配,这意味着您可以按需调度任意数量的任务,而无需专门了解有多少 CPU 核心可用。这与其他依赖于线程池等技术的作业系统不同,后者更容易以低效率的方式创建超出 CPU 核心数量的线程。
作业系统会使用”工作窃取”作为其调度策略的一部分,以均衡工作线程所共享的任务量。工作线程可能比其他线程更快地处理任务,因此,当某条工作线程处理完所有任务后,它会查看其他工作线程的队列,然后处理被分配给另一条工作线程的任务。
为简化多线程代码的编写,作业系统引入了一套安全系统,可以检测所有潜在的竞争条件,并避免可能导致的相应错误。当某项操作的输出取决于不受其控制的另一个过程的时序时,就会出现竞争条件。
例如,如果作业系统将主线程中对代码数据的引用发送给某项作业,那么它就无法验证主线程是否会在该项作业对该数据进行写入的同时读取数据。此时就会产生竞争条件。
为解决这个问题,作业系统会向每项作业分别发送其操作所需的数据副本,而不是对主线程中的数据进行引用。这种副本可以隔离数据,从而消除竞争条件。
作业系统复制数据的方式意味着作业只能访问 blittable 数据类型。这些类型不需要进行转换即可在托管代码和原生代码之间传递。
作业系统会使用 memcpy 复制 blittable 类型,并在 Unity 的托管部分和原生部分之间传输数据。在调度作业时,系统会使用 memcpy 将数据放入原生内存,并在执行作业时让托管端访问该副本。详情请参阅调度作业。
除核心 Unity 引擎中提供的作业系统外,Collections 包还扩展了许多作业类型和原生容器。详情请参阅 Collections 文档。