Version: 6000.3
语言: 中文
作业概述
作业依赖关系

创建和运行作业

若要创建并成功运行作业,必须执行以下作:

  • 创建作业:实现IJob接口。
  • 计划作业:调用Schedule工作中的方法。
  • 等待作业完成:如果作业已经完成,它会立即返回,您可以调用Complete方法。

创建作业

要在 Unity 中创建作业,请实现IJob接口。您可以使用您的IJob实现以计划与正在运行的任何其他作业并行运行的单个作业。

IJob有一个必需的方法:Execute,每当工作线程运行作业时,Unity 都会调用该作业。

创建作业时,还可以创建JobHandle对于它,需要使用哪些其他方法来引用作业。

重要:没有防止访问非只读或可变您可以更改可变包的内容。这是不可变的反义词。只有本地包嵌入式包可变的。
请参阅术语表
作业中的静态数据。访问此类数据会规避所有安全系统,并可能导致您的应用程序或 Unity 编辑器崩溃。

当 Unity 运行时,作业系统会复制计划的作业数据,从而防止多个线程读取或写入相同的数据。仅写入NativeContainer可以在作业完成后访问。这是因为NativeContainer作业使用的和原始NativeContainer对象指向同一内存。有关详细信息,请参阅有关线程安全类型的文档。

当作业系统从其作业队列中选取作业时,它会运行Execute方法在单个线程上一次。通常,作业系统在后台线程上运行作业,但如果主线程处于空闲状态,它可以选择主线程。因此,您应该将作业设计为在框架下完成。

计划作业

要计划作业,请调用Schedule.这会将作业放入作业队列中,一旦作业的所有依赖项(如果有)完成,作业系统就会开始执行作业。一旦安排,就无法中断作业。您只能调用Schedule从主线程。

提示:作业有一个Run您可以使用的方法代替Schedule立即在主线程上执行作业。您可以将其用于调试目的。

完成作业

一旦你调用Schedule并且作业系统已经执行了作业,可以调用Complete方法JobHandle以访问作业中的数据。最佳做法是调用Complete尽可能晚。当您调用Complete,主线程可以安全地访问NativeContainer作业正在使用的实例。叫Complete还清理了安全系统中的状态。不这样做会导致内存泄漏。

工作示例

下面是将两个浮点值相加的作业示例。它实现IJob,使用NativeArray获取作业结果,并使用Execute方法,其中包含其中作业的实现:

using UnityEngine;
using Unity.Collections;
using Unity.Jobs;

// Job adding two floating point values together
public struct MyJob : IJob
{
    public float a;
    public float b;
    public NativeArray<float> result;

    public void Execute()
    {
        result[0] = a + b;
    }
}

以下示例基于MyJobjob 在主线程上调度作业:

using UnityEngine;
using Unity.Collections;
using Unity.Jobs;

public class MyScheduledJob : MonoBehaviour
{
    // Create a native array of a single float to store the result. Using a
    // NativeArray is the only way you can get the results of the job, whether
    // you're getting one value or an array of values.
    NativeArray<float> result;
    // Create a JobHandle for the job
    JobHandle handle;

    // Set up the job
    public struct MyJob : IJob
    {
        public float a;
        public float b;
        public NativeArray<float> result;

        public void Execute()
        {
            result[0] = a + b;
        }
    }

    // Update is called once per frame
    void Update()
    {
        // Set up the job data
        result = new NativeArray<float>(1, Allocator.TempJob);

        MyJob jobData = new MyJob
        {
            a = 10,
            b = 10,
            result = result
        };

        // Schedule the job
        handle = jobData.Schedule();
    }

    private void LateUpdate()
    {
        // Sometime later in the frame, wait for the job to complete before accessing the results.
        handle.Complete();

        // All copies of the NativeArray point to the same memory, you can access the result in "your" copy of the NativeArray
        // float aPlusB = result[0];

        // Free the memory allocated by the result array
        result.Dispose();
    }


}

安排和完成最佳做法

最佳做法是调用Schedule在作业上获得所需的数据后立即调用,并且不要调用Complete直到您需要结果。

您可以在框架中不与更重要的工作竞争的部分安排不太重要的工作。

例如,如果一帧结束和下一帧开始之间有一段时间没有作业正在运行,并且可以接受一帧延迟,则可以将作业安排在帧结束,并在下一帧中使用其结果。或者,如果您的应用程序使该转换期与其他作业相匹配,并且框架中的其他位置有一个未充分利用的时期,那么将您的作业安排在那里会更有效。

您还可以使用分析器帮助您优化游戏的窗口。它显示了在游戏的各个领域花费了多少时间。例如,它可以报告渲染、动画制作或游戏逻辑所花费的时间百分比。更多信息
请参阅术语表
查看 Unity 在哪里等待作业完成。标记WaitForJobGroupID在主线程上表明了这一点。此标记可能意味着你在某处引入了应该解决的数据依赖关系。查找JobHandle.Complete以追踪强制主线程等待的数据依赖关系。

避免使用长时间运行的作业

与线程不同,作业不产生执行。作业启动后,该作业工作线程将承诺在运行任何其他作业之前完成作业。因此,最佳做法是将长时间运行的作业分解为相互依赖的较小作业,而不是提交相对于系统中其他作业需要很长时间才能完成的作业。

作业系统通常运行多个作业依赖项链,因此,如果将长时间运行的任务分解为多个部分,则多个作业链就有可能取得进展。相反,如果作业系统充满了长时间运行的作业,它们可能会完全消耗所有工作线程并阻止独立作业执行。这可能会推送主线程显式等待的重要作业的完成时间,从而导致主线程上停滞,否则这些停滞不会存在。

特别是,长时间运行IJobParallelFor作业会对作业系统产生负面影响,因为这些作业类型有意尝试在尽可能多的工作线程上运行,以实现作业批处理大小。如果无法分解长时间的并行作业,请考虑在计划作业时增加作业的批大小,以限制选择长时间运行作业的辅助角色数量。

MyParallelJob jobData = new MyParallelJob();
jobData.Data = someData;  
jobData.Result = someArray;  

// Use half the available worker threads, clamped to a minimum of 1 worker thread
const int numBatches = Math.Max(1, JobsUtility.JobWorkerCount / 2); 
const int totalItems = someArray.Length;
const int batchSize = totalItems / numBatches;

// Schedule the job with one Execute per index in the results array and batchSize items per processing batch
JobHandle handle = jobData.Schedule(result.Length, totalItems, batchSize);

其他资源

作业概述
作业依赖关系