返回

JobSystemAPI讲解

前言

JobSystem并非必须配合ECS使用,他完全可以独立运行在游戏项目中,配合Burst编译能够最大化为我们项目提供多线程支持。使用也较为简单,可以直接视为类似DoTwenn一类的插件放入项目中使用

当前官方还未将JobSystem定位正式版,所以在官方文档上很多地方都较含糊(例如:IJobParallelForBatch、IJobParallelForDefer等),而且还存在一些版本迭代下冗余的方法。

Job概念

Job并非线程,他是运行在线程上的一个个任务(理解为方法更方便,但它是个结构体)。

JobSystem的组成部分有:IJob及其相关变种,JobHandle,NativeArray及其相关变种,Burst特性。

JobHandle

JobHandle即Job在执行过程中返回给我们的操作的状态。最直观来说我们可以通过JobHandle查看Job是否执行完毕。再就是,可以将JobHandle作为其他Job启动的依赖项,这样可以确保后者Job会在JobHandle为执行完毕状态后执行,而非并行执行。

常用技巧

变量

  • IsCompleted: 如果该任务当前正在运行,则返回 false。如果该任务已完成,则返回 true。

公共函数

  • Complete:在主线程调用该方法后,会等待该Job执行完毕后再执行后续方法。

静态函数:

  • public static JobHandle CombineDependencies (JobHandle job0, JobHandle job1);

    public static JobHandle CombineDependencies (NativeArray jobs);

    可以将多个JobHandle合并为一个,然后作为其他Job启动时的JobHandle依赖

  • public static CompleteAll CombineDependencies (ref JobHandle job0, ref JobHandle job1);

    public static CompleteAll CombineDependencies (NativeArray jobs);

    在主线程等待所有传入JobHandle为执行完毕状态后,再执行后续方法。

NativeContainer

NativeContainer 是一种托管值类型,为本机内存提供了一个相对安全的 C# 封装器。它包含一个指向非托管分配的指针。

考虑到Job运行在多线程上,会出现数据安全问题——竞争条件(这个不理解的同学需要补一点多线程基础);且Job在运行完毕后会释放自身的数据内存(使用玩即释放),主线程无法访问Job中数据;所以现在需要一种共享内存容器来让主线程与Job共享数据。

NativeContainer 允许在Job中访问与主线程共享的数据,而不是使用常规的引用或内存拷贝。

具体的应用方法见后续IJob介绍部分。

NativeArray

NativeArray<T> array = new NativeArray<T>(int capacity, Allocator allovator);

除构造参数不同,使用上与普通的Array无异。

第一个构造参数count为数组大小,第二个参数allovator为分配内存的存活周期(详情见后文)

需要特别注意:NativeArray在使用完后要调用其array.Dispose()方法来释放内存,否则会出现内存泄漏。

Allocator

用于为 NativeArray 指定分配类型。其中Allocator.Temp、Allocator.TempJob、Allocator.Persistent为常用类型

  • Allocator.Invalid:无效分配
  • Allocator.None:未分配
  • Allocator.Temp:临时分配,NativeArray存活周期为1帧。
  • Allocator.TempJob:临时Job分配,NativeArray存活周期为4帧,如果第4帧没有调用NativeArray.Dispose,则会报警告
  • Allocator.Persistent:永久分配
  • Allocator.AudioKernel:与音频相关。

ReadOnly

[ReadOnly]
public NativeArray<int> array;

标记NativeArray在Job中为只读的,默认NativeArray是可读可写的。

NativeArray的其他变种

NativeList

var list = new NativeList<T>(Allocator allocator);

可调整大小的 NativeArray

NativeHashMap

var hashMap = new NativeHashMap<TKey, TValue>(int capacity, Allocator allocator);

键/值对。

NativeMultiHashMap

var multiHashMap = new NativeMultiHashMap<TKey, TValue>(int capacity, Allocator allocator);

每个键有多个值。

NativeQueue

var queue = new NativeQueue<T>(Allocator allocator);

先进先出队列。

IJob

记住所有的IJob都是数据结构。

IJob

IJob特征是将单个Job放在一个线程上去执行,这个线程是随机的(也可能是主线程)。

创建IJob

        [BurstCompile]
        struct My_IJob : IJob
        {
            [ReadOnly]
            public NativeArray<int> array;

            public float deltaTime;          

            public NativeArray<int> count;

            public void Execute()
            {
                for (int i = 0; i < array.Length; i++)
                {
                    count[0] += array[i];
                }
            }
        }

我们需要声明一个结构体,并继承IJob,在结构体内部实现Execute方法。

Execute方法内部即我们需要进行计算的代码。

[BurstCompile]特性有助于我们启动Burst编译时优化该Job。

使用IJob

void Update()
{
    int a = 0;

    var array = new NativeArray<int>(1000, Allocator.TempJob);
    var count = new NativeArray<int>(1, Allocator.TempJob);

    for (int i = 0; i < 1000; i++)
    {
        array[i] = 1;
    }

    var job = new My_IJob()
    {
        array = array,
        deltaTime = Time.deltaTime,
        count = count
    };

    //1.随机线程执行
    JobHandle jobHandle = job.Schedule();
    jobHandle.Complete();

    //2.直接在当前线程执行Job
    //job.Run();               

    a = job.count[0];

    array.Dispose();
    count.Dispose();
    Debug.Log("job:" + a);
}

job.Schedule();来执行Job,并返回一个JobHandle。

通过调用jobHandle.Complete();方法来等待该Job执行完毕。

关于NativeArray,再次声明需要在使用完毕后调用array.Dispose();释放内存。

完整示范

在计算过程中使用到了math,需要在Unity中导入Mathematics包。

切换OpenJob可以看到Job开启关闭的效果(观察FPS)。

(由于IJob性能上与直接主线程执行没有什么区别,所以看不到太大差距)

using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;

namespace Demo1
{
    class Test_IJob : MonoBehaviour
    {
        [BurstCompile]
        struct My_IJob : IJob
        {
            [ReadOnly]
            public NativeArray<int> array;

            public float deltaTime;

            //public int count;
            //从主线程中访问数据,只能通过Native,所以上面这样直接访问内存是不允许的,只能通过下面这样
            public NativeArray<int> count;

            public void Execute()
            {
                for (int i = 0; i < array.Length; i++)
                {
                    count[0] += Compute(i);
                }
            }
        }

        public bool OpenJob;

        void Update()
        {
            int a = 0;
            //Ijob只是将任务放在一个线程上执行(也有可能是主线程)
            //性能上与在主线程执行并无区别
            if (OpenJob)
            {        
                var array = new NativeArray<int>(1000, Allocator.TempJob);
                var count = new NativeArray<int>(1, Allocator.TempJob);

                for (int i = 0; i < 1000; i++)
                {
                    array[i] = 1;
                }
             
                var job = new My_IJob()
                {
                    array = array,
                    deltaTime = Time.deltaTime,
                    count = count
                };

                //1.随机线程执行
                JobHandle jobHandle = job.Schedule();
                jobHandle.Complete();

                //2.直接在当前线程执行Job
                //job.Run();               

                a = job.count[0];

                array.Dispose();
                count.Dispose();
                Debug.Log("job:"+a);
            }
            else
            {
                int z = 0;
                for (int i = 0; i < 1000; i++)
                {
                    z += Compute(i);
                }
                a = z;
                Debug.Log(a);
            }            
        }

        static int Compute(int i)
        {
            // 耗时的计算代码
            double value = 0f;
            for (int j = 0; j < 1000; j++)
            {
                value = math.exp10(math.sqrt(value));
            }
            return i;
        }
    }
}

IJobFor

IJobFor有三种执行方式,根据执行方式不同,所展现性能也不同。

  • void Run(int arrayLength)

    直接在主线程上执行, arrayLength为执行Job Execute方法的迭代总次数

  • JobHandle Schedule(int arrayLength, JobHandle dependency)

    多个线程顺序执行多个Job,由于是顺序执行,需要等待上个Job执行完成才能执行下个Job,所以性能上与在主线程执行并无区别。 arrayLength为执行Job Execute方法的迭代总次数,dependency为依赖的JobHandle

  • JobHandle ScheduleParallel(int arrayLength, int innerloopBatchCount, JobHandle dependency)

    多个线程同时执行多个Job,是并行运行的,所以性能有所提升。 arrayLength为执行Job Execute方法的迭代总次数,innerloopBatchCount为单个Job需要负责的迭代次数,dependency为依赖的JobHandle,

    简单理接参数:我们原本打算执行100次Job的 Execute方法,那么arrayLength填100。我们希望每迭代20次为一个包(这个包就是Job),那么innerloopBatchCount填入20。这样Job在启动时会自动将100次迭代分成5个20次迭代的Job,然后将这5个Job分发到电脑的线程中,假设我们电脑是四核心,那么就三个线程执行一个Job,还有一个线程执行两个Job,加起来一个五个Job、100次迭代。当然那个线程执行两个Job完全是随机的。

完整示范

using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;

public class Test_IJobFor : MonoBehaviour
{
    [BurstCompile]
    struct My_IJobFor : IJobFor
    {
        [ReadOnly]
        public NativeArray<int> array;

        public float deltaTime;

        public NativeArray<int> count;

        public void Execute(int index)
        {
            count[index] = Compute(index);
        }
    }

    public bool OpenJob;

    private void Update()
    {
        int a = 0;

        if (OpenJob)
        {
            var array = new NativeArray<int>(1000, Allocator.TempJob);
            var count = new NativeArray<int>(1000, Allocator.TempJob);

            for (int i = 0; i < 1000; i++)
            {
                array[i] = 1;
            }

            var job = new My_IJobFor()
            {
                array = array,
                deltaTime = Time.deltaTime,
                count = count
            };

            JobHandle sheduleJobDependency = new JobHandle();

            ///三种执行方式:

            //1.多个线程顺序执行多个Job,由于是顺序执行,需要等待上个Job执行完成才能执行下个Job,所以性能上与在主线程执行并无区别
            //JobHandle sheduleJobHandle = job.Schedule(array.Length, sheduleJobDependency);

            //2.并行 (迭代总次数,每个Job执行负责迭代次数,依赖的Job句柄)
            //将遍历总次数按批次平分成包,将不同的包放入不同线程中执行(有可能是主线程)。
            JobHandle sheduleJobHandle = job.ScheduleParallel(array.Length, 10, sheduleJobDependency);

            sheduleJobHandle.Complete();

            //2.直接在当前线程执行Job
            //job.Run(array.Length);   

            for (int i = 0; i < count.Length; i++)
            {
                a += count[i];
            }

            array.Dispose();
            count.Dispose();

            Debug.Log("job:" + a);
        }
        else
        {
            int z = 0;
            for (int i = 0; i < 1000; i++)
            {
                z += Compute(i);
            }
            a = z;
            Debug.Log(a);
        }
    }

    static int Compute(int i)
    {
        // 耗时的计算代码
        double value = 0f;
        for (int j = 0; j < 1000; j++)
        {
            value = math.exp10(math.sqrt(value));
        }

        return i;
    }
}

IJobParallelFor

IJobParallelFor执行效率同IJobFor.ScheduleParallel(),都是并行执行,但是精简了JobHandle依赖参数。可以说IJobFor.ScheduleParallel()是版本迭代下来冗余的产物,完全可以被IJobParallelFor取代。

完整示范

using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;

public class Test_IJobParallelFor : MonoBehaviour
{
    [BurstCompile]
    struct My_IJobParallelFor : IJobParallelFor
    {
        [ReadOnly]
        public NativeArray<int> array;

        public float deltaTime;

        public NativeArray<int> count;

        public void Execute(int index)
        {
            count[index] = Compute(index);
        }
    }

    public bool OpenJob;

    private void Update()
    {
        int a = 0;

        if (OpenJob)
        {
            var array = new NativeArray<int>(1000, Allocator.TempJob);
            var count = new NativeArray<int>(1000, Allocator.TempJob);

            for (int i = 0; i < 1000; i++)
            {
                array[i] = 1;
            }

            var job = new My_IJobParallelFor()
            {
                array = array,
                deltaTime = Time.deltaTime,
                count = count
            };

            //1.在多线程中执行job (迭代总次数,每个Job执行负责迭代次数)
            JobHandle jobHandle = job.Schedule(array.Length, 10);
            jobHandle.Complete();

            //2.直接在当前线程执行Job
            //job.Run(array.Length);   

            for (int i = 0; i < count.Length; i++)
            {
                a += count[i];
            }

            array.Dispose();
            count.Dispose();

            Debug.Log("job:" + a);
        }
        else
        {
            int z = 0;
            for (int i = 0; i < 1000; i++)
            {
                z += Compute(i);
            }
            a = z;
            Debug.Log(a);
        }
    }

    static int Compute(int i)
    {
        // 耗时的计算代码
        double value = 0f;
        for (int j = 0; j < 1000; j++)
        {
            value = math.exp10(math.sqrt(value));
        }

        return i;
    }
}

IJobParallelForTransform

IJobParallelForTransform可以在Job运行时提供transform读写支持。

TransformAccessArray

TransformAccessArray是一个可以在Job和主线程中共享Transform的List容器,可以理接为List<Transform>

var accessArray = new TransformAccessArray(Transform[] transforms, int desiredJobCount = -1);
var accessArray = new TransformAccessArray(int capacity, int desiredJobCount = -1);

构造参数:capacity为初始话容量,后续容量不够会自动扩充,这点与List类似。desiredJobCount为单个Job负责迭代次数。

作为多线程共享容器,与NativeArray相同,需要在使用完毕后调用accessArray.Dispose();方法释放内存。

在调用IJobParallelForTransform.Schedule()方法执行Job时TransformAccessArray作为参数提供支持

JobHandle jobHandle = job.Schedule(accessArray);

完整案例

using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Jobs;

public class Test_IJobParallelForTransform : MonoBehaviour
{
    [BurstCompile]
    struct My_IJobParallelForTransform : IJobParallelForTransform
    {
        [ReadOnly]
        public NativeList<Vector3> list;

        public float deltaTime;

        public void Execute(int index, TransformAccess transform)
        {
            transform.position += (list[index]*deltaTime);
            Compute(index);
        }
    }

    public bool OpenJob;
    public GameObject prefab;
    public int prefabCount=1000;

    private TransformAccessArray accessArray;
    private NativeList<Vector3> list;

    private void Start()
    {
        //参数:初始容量(不够了会自动扩充),迭代批次
        accessArray = new TransformAccessArray(prefabCount,20);
        list = new NativeList<Vector3>(Allocator.Persistent);
        for (int i = 0; i < prefabCount; i++)
        {
            var transform = GameObject.Instantiate(this.prefab, UnityEngine.Random.insideUnitSphere * 100, Quaternion.identity).transform;
            accessArray.Add(transform);
            list.Add(UnityEngine.Random.insideUnitSphere);
        }
    }

    private void Update()
    {
        if (OpenJob)
        {
            var job = new My_IJobParallelForTransform()
            {
                list = list,
                deltaTime = Time.deltaTime               
            };

            JobHandle jobHandle = job.Schedule(accessArray);
            jobHandle.Complete();

            Debug.Log("job");
        }
        else
        {
            for (int i = 0; i < accessArray.length; i++)
            {
                accessArray[i].position += (list[i] * Time.deltaTime);
                Compute(i);
            }
            Debug.Log("");
        }
    }

    static int Compute(int i)
    {
        // 耗时的计算代码
        double value = 0f;
        for (int j = 0; j < 1000; j++)
        {
            value = math.exp10(math.sqrt(value));
        }

        return i;
    }

    private void OnDestroy()
    {
        //记得释放内存
        accessArray.Dispose();
        list.Dispose();
    }

}

IJobParallelForBatch

IJobParallelForBatch与IJobParallelFor最大区别就是可以跨批次迭代。

通俗点理接见下面代码:

    void IJobParallelFor.Schedule(int arrayLength, int innerloopBatchCount)
    {
        for (int i = 0; i < arrayLength; i++)
        {
            Job.Execute(i);
        }
    }

    void IJobParallelForBatch.Schedule(int arrayLength, int innerloopBatchCount)
    {        
        for (int i = 0; i < (arrayLength/innerloopBatchCount); i++)
        {           
            Job.Execute(i, innerloopBatchCount);            
        }        
    }

完整案例

using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;

public class Test_IJobParallelForBatch : MonoBehaviour
{
    [BurstCompile]
    struct My_IJobParallelForBatch : IJobParallelForBatch
    {
        [ReadOnly]
        public NativeArray<int> array;

        public float deltaTime;

        public NativeArray<int> sum;

        public void Execute(int startIndex, int count)
        {
            Debug.Log($"startIndex:{startIndex},count:{count}");

            for (int i = startIndex; i < startIndex + count; i++)
            {
                sum[i] = Compute(i);
            }           
        }
    }

    [BurstCompile]
    struct My_IJobParallelFor : IJobParallelFor
    {
        [ReadOnly]
        public NativeArray<int> array;

        public float deltaTime;

        public NativeArray<int> count;

        public void Execute(int index)
        {
            Debug.Log($"index{index}");
            count[index] = Compute(index);
        }
    }

    public bool OpenForBatch;

    private void Update()
    {
        int a = 0;

        if (OpenForBatch)
        {
            var array = new NativeArray<int>(23, Allocator.TempJob);
            var count = new NativeArray<int>(23, Allocator.TempJob);

            for (int i = 0; i < 23; i++)
            {
                array[i] = 1;
            }

            var job = new My_IJobParallelForBatch()
            {
                array = array,
                deltaTime = Time.deltaTime,
                sum = count
            };

            //在多线程中执行job (迭代总次数,每个Job执行负责迭代次数)
            JobHandle jobHandle = job.ScheduleBatch(array.Length, 5);
            jobHandle.Complete();

            for (int i = 0; i < count.Length; i++)
            {
                a += count[i];
            }

            array.Dispose();
            count.Dispose();

            Debug.Log("ForBatch:::::::" + a);
        }
        else
        {
            var array = new NativeArray<int>(23, Allocator.TempJob);
            var count = new NativeArray<int>(23, Allocator.TempJob);

            for (int i = 0; i < 23; i++)
            {
                array[i] = 1;
            }

            var job = new My_IJobParallelFor()
            {
                array = array,
                deltaTime = Time.deltaTime,
                count = count
            };

            //在多线程中执行job (迭代总次数,每个Job执行负责迭代次数)
            JobHandle jobHandle = job.Schedule(array.Length, 5);
            jobHandle.Complete();           

            for (int i = 0; i < count.Length; i++)
            {
                a += count[i];
            }

            array.Dispose();
            count.Dispose();

            Debug.Log("JobFor:::::::" + a);
        }
    }

    static int Compute(int i)
    {
        // 耗时的计算代码
        double value = 0f;
        for (int j = 0; j < 1000; j++)
        {
            value = math.exp10(math.sqrt(value));
        }

        return i;
    }
}

IJobParallelForDefer

IJobParallelForDefer与IJobParallelFor使用方法基本相同,唯一不同的是传入的迭代总次数为指针,并且可以在调用job.Schedule();方法后修改指针,以此来达到Job运行时动态修改迭代次数的目的。

需要注意的是,在C#中使用指针需要在Unity中开启不安全代码许可,并在方法前加上unsafe关键字。

完整案例

using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;

public class Test_IJobParallelForDefer : MonoBehaviour
{
    [BurstCompile]
    struct My_IJobParallelForDefer : IJobParallelForDefer
    {
        [ReadOnly]
        public NativeArray<int> array;

        public float deltaTime;

        public NativeArray<int> count;

        public void Execute(int index)
        {
            Debug.Log($"index{index}");
            count[index] = Compute(index);
        }
    }

    [BurstCompile]
    struct My_IJobParallelFor : IJobParallelFor
    {
        [ReadOnly]
        public NativeArray<int> array;

        public float deltaTime;

        public NativeArray<int> count;

        public void Execute(int index)
        {
            Debug.Log($"index{index}");
            count[index] = Compute(index);
        }
    }

    public bool OpenForDefer;

    unsafe private void Update()
    {
        int a = 0;

        if (OpenForDefer)
        {
            int length = 25;
            int* _Length = &length;

            var array = new NativeArray<int>(length, Allocator.TempJob);
            var count = new NativeArray<int>(length, Allocator.TempJob);

            for (int i = 0; i < length; i++)
            {
                array[i] = 1;
            }

            var job = new My_IJobParallelForDefer()
            {
                array = array,
                deltaTime = Time.deltaTime,
                count = count
            };

            //在多线程中执行job (迭代总次数,每个Job执行负责迭代次数)
            //可以传入指针,允许在调用Schedule之后修改指针引用来改变遍历次数
            JobHandle jobHandle = job.Schedule(_Length, 5);
            length = 20;

            jobHandle.Complete();

            for (int i = 0; i < count.Length; i++)
            {
                a += count[i];
            }

            array.Dispose();
            count.Dispose();

            Debug.Log("ForDefer:::::::" + a);
        }
        else
        {
            int length = 25;

            var array = new NativeArray<int>(length, Allocator.TempJob);
            var count = new NativeArray<int>(length, Allocator.TempJob);

            for (int i = 0; i < length; i++)
            {
                array[i] = 1;
            }

            var job = new My_IJobParallelFor()
            {
                array = array,
                deltaTime = Time.deltaTime,
                count = count
            };

            //在多线程中执行job (迭代总次数,每个Job执行负责迭代次数)
            JobHandle jobHandle = job.Schedule(length, 5);
            length = 20;
            jobHandle.Complete();

            for (int i = 0; i < count.Length; i++)
            {
                a += count[i];
            }

            array.Dispose();
            count.Dispose();

            Debug.Log("JobFor:::::::" + a);
        }
    }

    static int Compute(int i)
    {
        // 耗时的计算代码
        double value = 0f;
        for (int j = 0; j < 1000; j++)
        {
            value = math.exp10(math.sqrt(value));
        }

        return i;
    }
}

IJobParallelForFilter

IJobParallelForFilter用于对NativeList<int>的元素进行筛选操作。

JobParallelForFilter.Execute()方法会返回一个bool参数,job会根据bool的真假操作List中的该元素。

bool IJobParallelForFilter.Execute(int index){
}

IJobParallelForFilter有两种执行方式:

  • IJobParallelForFilter(NativeList indices, int innerloopBatchCount)

    该方法会在job的Execute(int index)返回为false时,调用参数indices.Remove(index)的方法,删除List中该元素。

    需要注意的是,这时候IJobParallelForFilter.Execute(int index)中的参数index并非List的指数,而是List的元素。

  • ScheduleAppend(NativeList indices, int arrayLength, int innerloopBatchCount)

    该方法会在job的Execute(int index)返回为true时,调用参数indices.Add(index)的方法,将指数作为元素添加到List中。

    将当前遍历的指数(即IJobParallelForFilter.Execute(int index)index)为从 0 到参数arrayLength。

完整案例

using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine;

public class Test_IJobParallelForFilter : MonoBehaviour
{
    [BurstCompile]
    struct My_IJobParallelForFilter : IJobParallelForFilter
    {
        [ReadOnly]
        public NativeArray<int> array;

        public float deltaTime;

        //需要注意的是,这里的index并非传入List<int>的指针,而是List<int>的元素
        bool IJobParallelForFilter.Execute(int index)
        {
            Compute(index);
            //如果Index为偶数返回真
            if (array[index] % 2 != 0)
                return false;
            return true;
        }
    }

    public bool FilterOrAppend;

    unsafe private void Update()
    {
        int length = 25;

        if (FilterOrAppend)
        {
            var list = new NativeList<int>(length,Allocator.TempJob);
            var array = new NativeArray<int>(length, Allocator.TempJob);

            for (int i = 0; i < length; i++)
            {
                list.Add(i);
                array[i] = i+100;
            }

            var job = new My_IJobParallelForFilter()
            {
                array = array,
                deltaTime = Time.deltaTime,
            };

            Debug.Log("执行前List容量:::::::" + list.Length);

            //在多线程中执行job (操作的List<int>,每个Job执行负责迭代次数)
            //该方法会根据Job的Execute()方法中返回的Bool,对List<int>的元素进行剔除。
            JobHandle jobHandle = job.ScheduleFilter(list, 5);
         
            jobHandle.Complete();

            for (int i = 0; i < list.Length; i++)
            {
                Debug.Log("ScheduleFilter:"+list[i]);
            }

            Debug.Log("执行后List容量:::::::" + list.Length);

            array.Dispose();
            list.Dispose();           
        }
        else
        {
            var list = new NativeList<int>(length, Allocator.TempJob);
            var array = new NativeArray<int>(length, Allocator.TempJob);

            for (int i = 0; i < length; i++)
            {
                array[i] = i+100;
            }

            var job = new My_IJobParallelForFilter()
            {
                array = array,
                deltaTime = Time.deltaTime,
            };

            Debug.Log("执行前List容量:::::::" + list.Length);

            //在多线程中执行job (操作的List<int>,迭代总次数,每个Job执行负责迭代次数)
            //该方法会根据Job的Execute()方法中返回的Bool,增加将迭代指数(即Index)Add到List<int>中。
            JobHandle jobHandle = job.ScheduleAppend(list,length, 5);
          
            jobHandle.Complete();

            for (int i = 0; i < list.Length; i++)
            {
                Debug.Log("ScheduleAppend:" + list[i]);
            }

            Debug.Log("执行后List容量:::::::" + list.Length);

            array.Dispose();
            list.Dispose();
        }
    }

    static int Compute(int i)
    {
        // 耗时的计算代码
        double value = 0f;
        for (int j = 0; j < 1000; j++)
        {
            value = math.exp10(math.sqrt(value));
        }

        return i;
    }
}

Burst

Burst编译系统,开启后能够进一步优化C#的语法,提升Job的运行性能。

前面声明Job结构体时我们已经看到了,在结构体上方有[BurstCompile]特性

     [BurstCompile]
    struct My_IJobFor : IJobParallelFor
    {
        ......

现在我们在Unity中开启它,在菜单栏-》Jobs-》Burst 下勾选Enable Commpilation

Licensed under CC BY-NC-SA 4.0
0