Job System 教程

简介

Job System最近在Unity2018.1中首次亮相,并将开始改变所有Unity脚本的编写方式。与即将推出的ECS和Burst编译器一起,旧的基于行为的编程范式将最终被取代。今天的文章是关于如何开始学习编写Unity脚本的新方法的教程。

可用的类型

任何blittable类型和类型都可以用于任何job中。类型既是可blittable类型又是值类型,则必须是一个原始(例如int)、枚举、指针,或仅包含原始、枚举、指针和blittable结构的结构体。

这个定义排除了所有引用类型。这意味着作业不能使用类、接口、委托、动态类型、字符串和(托管)数组。本机集合,如NativeArray和NativeSlice,可以在Job中使用。还可以使用定制的本机集合类型,类似NativeHashMap的更多类型将在Unity的未来版本中可用,特别是与ECS(实体-组件-系统),后者目前处于“预览”状态。

可用的API

由于jobs离开了主线程,他们不能访问Unity API的大部分。只能使用Unity API的特定部分。据我所知,截至2018.1,唯一有文档记录的安全的非实验性API是Transform,它必须通过TransformAccess和TransformAccessArray访问。下面我们将看到如何做到这一点。除了这个文档化的API,我们还可以通过Unity的c#源代码找到更多可以在主线程之外使用的功能。搜索[ThreadSafe]属性或者IsThreadSafe设置为true的[FreeFunction]属性可以找到很多有用的函数。
截至2018.1.0f2,以下是所有线程安全的函数:

UnityEngine.CoreModule/UnityEngine.iOS/Device.cs-        public static extern void SetNoBackupFlag(string path);
UnityEngine.CoreModule/UnityEngine.iOS/Device.cs-        public static extern void ResetNoBackupFlag(string path);
UnityEngine.CoreModule/UnityEngine.iOS/Device.cs-        public static extern bool RequestStoreReview();
UnityEngine.CoreModule/UnityEngine.iOS/NotificationHelper.cs-        internal static extern void DestroyLocal(IntPtr target);
UnityEngine.CoreModule/UnityEngine.iOS/NotificationHelper.cs-        internal static extern void DestroyRemote(IntPtr target);
UnityEngine.CoreModule/UnityEngine.iOS/OnDemandResourcesRequest.cs-        private static extern void DestroyFromScript(IntPtr ptr);
UnityEngine.CoreModule/UnityEngine/Matrix4x4.cs-        private Quaternion GetRotation()
UnityEngine.CoreModule/UnityEngine/Matrix4x4.cs-        private Vector3 GetLossyScale()
UnityEngine.CoreModule/UnityEngine/Matrix4x4.cs-        private bool IsIdentity()
UnityEngine.CoreModule/UnityEngine/Matrix4x4.cs-        private float GetDeterminant()
UnityEngine.CoreModule/UnityEngine/Matrix4x4.cs-        private FrustumPlanes DecomposeProjection()
UnityEngine.CoreModule/UnityEngine/Matrix4x4.cs-        public bool ValidTRS()
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/AtomicSafetyHandle.cs-        public static AtomicSafetyHandle Create()
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/AtomicSafetyHandle.cs-        public static AtomicSafetyHandle GetTempUnsafePtrSliceHandle()
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/AtomicSafetyHandle.cs-        public static void Release(AtomicSafetyHandle handle)
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/AtomicSafetyHandle.cs-        public static extern void UseSecondaryVersion(ref AtomicSafetyHandle handle);
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/AtomicSafetyHandle.cs-        public static void SetAllowSecondaryVersionWriting(AtomicSafetyHandle handle, bool allowWriting)
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/AtomicSafetyHandle.cs-        public static EnforceJobResult EnforceAllBufferJobsHaveCompleted(AtomicSafetyHandle handle)
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/AtomicSafetyHandle.cs-        public static EnforceJobResult EnforceAllBufferJobsHaveCompletedAndRelease(AtomicSafetyHandle handle)
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/AtomicSafetyHandle.cs-        public static EnforceJobResult EnforceAllBufferJobsHaveCompletedAndDisableReadWrite(AtomicSafetyHandle handle)
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/AtomicSafetyHandle.cs-        public static int GetReaderArray(AtomicSafetyHandle handle, int maxCount, IntPtr output)
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/AtomicSafetyHandle.cs-        public static JobHandle GetWriter(AtomicSafetyHandle handle)
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/AtomicSafetyHandle.cs-        public static string GetReaderName(AtomicSafetyHandle handle, int readerIndex)
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/AtomicSafetyHandle.cs-        public static string GetWriterName(AtomicSafetyHandle handle)
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/UnsafeUtility.cs-        private static extern int GetFieldOffsetInStruct(FieldInfo field);
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/UnsafeUtility.cs-        private static extern int GetFieldOffsetInClass(FieldInfo field);
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/UnsafeUtility.cs-        public unsafe static extern void* PinGCObjectAndGetAddress(object target, out ulong gcHandle);
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/UnsafeUtility.cs-        public static extern void ReleaseGCObject(ulong gcHandle);
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/UnsafeUtility.cs-        public unsafe static extern void CopyObjectAddressToPtr(object target, void* dstPtr);
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/UnsafeUtility.cs-        public unsafe static extern void* Malloc(long size, int alignment, Allocator allocator);
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/UnsafeUtility.cs-        public unsafe static extern void Free(void* memory, Allocator allocator);
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/UnsafeUtility.cs-        public unsafe static extern void MemCpy(void* destination, void* source, long size);
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/UnsafeUtility.cs-        public unsafe static extern void MemCpyReplicate(void* destination, void* source, int size, int count);
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/UnsafeUtility.cs-        public unsafe static extern void MemCpyStride(void* destination, int destinationStride, void* source, int sourceStride, int elementSize, int count);
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/UnsafeUtility.cs-        public unsafe static extern void MemMove(void* destination, void* source, long size);
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/UnsafeUtility.cs-        public unsafe static extern void MemClear(void* destination, long size);
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/UnsafeUtility.cs-        public static extern int SizeOf(Type type);
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/UnsafeUtility.cs-        public static extern bool IsBlittable(Type type);
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/UnsafeUtility.cs-        internal static extern void LogError(string msg, string filename, int linenumber);
UnityEngine.CoreModule/UnityEngine/Bounds.cs-        public bool Contains(Vector3 point)
UnityEngine.CoreModule/UnityEngine/Bounds.cs-        public float SqrDistance(Vector3 point)
UnityEngine.CoreModule/UnityEngine/Bounds.cs-        private static bool IntersectRayAABB(Ray ray, Bounds bounds, out float dist)
UnityEngine.CoreModule/UnityEngine/Bounds.cs-        public Vector3 ClosestPoint(Vector3 point)
UnityEngine.CoreModule/UnityEngine/MaterialPropertyBlock.cs-        private static extern void DestroyImpl(IntPtr mpb);
UnityEngine.CoreModule/UnityEngine/Mathf.cs-        public static extern int ClosestPowerOfTwo(int value);
UnityEngine.CoreModule/UnityEngine/Mathf.cs-        public static extern bool IsPowerOfTwo(int value);
UnityEngine.CoreModule/UnityEngine/Mathf.cs-        public static extern int NextPowerOfTwo(int value);
UnityEngine.CoreModule/UnityEngine/Mathf.cs-        public static extern float GammaToLinearSpace(float value);
UnityEngine.CoreModule/UnityEngine/Mathf.cs-        public static extern float LinearToGammaSpace(float value);
UnityEngine.CoreModule/UnityEngine/Mathf.cs-        public static Color CorrelatedColorTemperatureToRGB(float kelvin)
UnityEngine.CoreModule/UnityEngine/Mathf.cs-        public static extern ushort FloatToHalf(float val);
UnityEngine.CoreModule/UnityEngine/Mathf.cs-        public static extern float HalfToFloat(ushort val);
UnityEngine.CoreModule/UnityEngine/Mathf.cs-        public static extern float PerlinNoise(float x, float y);
UnityEngine.CoreModule/UnityEngine/Matrix4x4.cs-        public static Matrix4x4 TRS(Vector3 pos, Quaternion q, Vector3 s)
UnityEngine.CoreModule/UnityEngine/Matrix4x4.cs-        public static Matrix4x4 Inverse(Matrix4x4 m)
UnityEngine.CoreModule/UnityEngine/Matrix4x4.cs-        public static Matrix4x4 Transpose(Matrix4x4 m)
UnityEngine.CoreModule/UnityEngine/Matrix4x4.cs-        public static Matrix4x4 Ortho(float left, float right, float bottom, float top, float zNear, float zFar)
UnityEngine.CoreModule/UnityEngine/Matrix4x4.cs-        public static Matrix4x4 Perspective(float fov, float aspect, float zNear, float zFar)
UnityEngine.CoreModule/UnityEngine/Matrix4x4.cs-        public static Matrix4x4 LookAt(Vector3 from, Vector3 to, Vector3 up)
UnityEngine.CoreModule/UnityEngine/Matrix4x4.cs-        public static Matrix4x4 Frustum(float left, float right, float bottom, float top, float zNear, float zFar)
UnityEngine.CoreModule/UnityEngine/MonoBehaviour.cs-        private static extern void ConstructorCheck([Writable] Object self);
UnityEngine.CoreModule/UnityEngine/Object.cs-        private static extern int GetOffsetOfInstanceIDInCPlusPlusObject();
UnityEngine.CoreModule/UnityEngine/Object.cs-        private static extern bool CurrentThreadIsMainThread();
UnityEngine.CoreModule/UnityEngine/Object.cs-        internal static extern bool DoesObjectWithInstanceIDExist(int instanceID);
UnityEngine.CoreModule/UnityEngine/Ping.cs-        private static extern void Internal_Destroy(IntPtr ptr);
UnityEngine.CoreModule/UnityEngine/Quaternion.cs-        public static Quaternion FromToRotation(Vector3 fromDirection, Vector3 toDirection)
UnityEngine.CoreModule/UnityEngine/Quaternion.cs-        public static Quaternion Inverse(Quaternion rotation)
UnityEngine.CoreModule/UnityEngine/Quaternion.cs-        public static Quaternion Slerp(Quaternion a, Quaternion b, float t)
UnityEngine.CoreModule/UnityEngine/Quaternion.cs-        public static Quaternion SlerpUnclamped(Quaternion a, Quaternion b, float t)
UnityEngine.CoreModule/UnityEngine/Quaternion.cs-        public static Quaternion Lerp(Quaternion a, Quaternion b, float t)
UnityEngine.CoreModule/UnityEngine/Quaternion.cs-        public static Quaternion LerpUnclamped(Quaternion a, Quaternion b, float t)
UnityEngine.CoreModule/UnityEngine/Quaternion.cs-        private static Quaternion Internal_FromEulerRad(Vector3 euler)
UnityEngine.CoreModule/UnityEngine/Quaternion.cs-        private static Vector3 Internal_ToEulerRad(Quaternion rotation)
UnityEngine.CoreModule/UnityEngine/Quaternion.cs-        private static void Internal_ToAxisAngleRad(Quaternion q, out Vector3 axis, out float angle)
UnityEngine.CoreModule/UnityEngine/Quaternion.cs-        public static Quaternion AngleAxis(float angle, Vector3 axis)
UnityEngine.CoreModule/UnityEngine/Quaternion.cs-        public static Quaternion LookRotation(Vector3 forward, [DefaultValue("Vector3.up")] Vector3 upwards)
UnityEngine.CoreModule/UnityEngine/Screen.cs-            Width { get; }
UnityEngine.CoreModule/UnityEngine/Screen.cs-            Height { get; }
UnityEngine.CoreModule/UnityEngine/ScriptableObject.cs-        private static extern void CreateScriptableObject([Writable] ScriptableObject self);
UnityEngine.CoreModule/UnityEngine/UnityLogWriter.cs-        private static extern void WriteStringToUnityLogImpl(string s);
UnityEngine.CoreModule/UnityEngine/Vector3.cs-        public static Vector3 Slerp(Vector3 a, Vector3 b, float t)
UnityEngine.CoreModule/UnityEngine/Vector3.cs-        public static Vector3 SlerpUnclamped(Vector3 a, Vector3 b, float t)
UnityEngine.CoreModule/UnityEngine/Vector3.cs-        private static extern void OrthoNormalize2(ref Vector3 a, ref Vector3 b);
UnityEngine.CoreModule/UnityEngine/Vector3.cs-        private static extern void OrthoNormalize3(ref Vector3 a, ref Vector3 b, ref Vector3 c);
UnityEngine.CoreModule/UnityEngine/Vector3.cs-        public static Vector3 RotateTowards(Vector3 current, Vector3 target, float maxRadiansDelta, float maxMagnitudeDelta)
UnityEngine.CoreModule/UnityEngine.Jobs/TransformAccessArray.cs-        internal static extern IntPtr GetSortedTransformAccess(IntPtr transformArrayIntPtr);
UnityEngine.CoreModule/UnityEngine.Jobs/TransformAccessArray.cs-        internal static extern IntPtr GetSortedToUserIndex(IntPtr transformArrayIntPtr);
UnityEngine.CoreModule/UnityEngine.Jobs/TransformAccess.cs-        private static extern void GetPosition(ref TransformAccess access, out Vector3 p);
UnityEngine.CoreModule/UnityEngine.Jobs/TransformAccess.cs-        private static extern void SetPosition(ref TransformAccess access, ref Vector3 p);
UnityEngine.CoreModule/UnityEngine.Jobs/TransformAccess.cs-        private static extern void GetRotation(ref TransformAccess access, out Quaternion r);
UnityEngine.CoreModule/UnityEngine.Jobs/TransformAccess.cs-        private static extern void SetRotation(ref TransformAccess access, ref Quaternion r);
UnityEngine.CoreModule/UnityEngine.Jobs/TransformAccess.cs-        private static extern void GetLocalPosition(ref TransformAccess access, out Vector3 p);
UnityEngine.CoreModule/UnityEngine.Jobs/TransformAccess.cs-        private static extern void SetLocalPosition(ref TransformAccess access, ref Vector3 p);
UnityEngine.CoreModule/UnityEngine.Jobs/TransformAccess.cs-        private static extern void GetLocalRotation(ref TransformAccess access, out Quaternion r);
UnityEngine.CoreModule/UnityEngine.Jobs/TransformAccess.cs-        private static extern void SetLocalRotation(ref TransformAccess access, ref Quaternion r);
UnityEngine.CoreModule/UnityEngine.Jobs/TransformAccess.cs-        private static extern void GetLocalScale(ref TransformAccess access, out Vector3 r);
UnityEngine.CoreModule/UnityEngine.Jobs/TransformAccess.cs-        private static extern void SetLocalScale(ref TransformAccess access, ref Vector3 r);
UnityEngine.CoreModule/Unity.Jobs.LowLevel.Unsafe/JobsUtility.cs-        public static extern bool GetWorkStealingRange(ref JobRanges ranges, int jobIndex, out int beginIndex, out int endIndex);
UnityEngine.CoreModule/Unity.Jobs.LowLevel.Unsafe/JobsUtility.cs-        public unsafe static extern void PatchBufferMinMaxRanges(IntPtr bufferRangePatchData, void* jobdata, int startIndex, int rangeSize);
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/AtomicSafetyHandle.cs-        public static void SetAllowReadOrWriteAccess(AtomicSafetyHandle handle, bool allowReadWriteAccess)
UnityEngine.CoreModule/Unity.Collections.LowLevel.Unsafe/AtomicSafetyHandle.cs-        public static bool GetAllowReadOrWriteAccess(AtomicSafetyHandle handle)
UnityEngine.AIModule/UnityEngine.Experimental.AI/NavMeshQuery.cs-        private static extern bool HasNodePool(IntPtr navMeshQuery);
UnityEngine.AIModule/UnityEngine.Experimental.AI/NavMeshQuery.cs-        private unsafe static PathQueryStatus BeginFindPath(IntPtr navMeshQuery, NavMeshLocation start, NavMeshLocation end, int areaMask, void* costs)
UnityEngine.AIModule/UnityEngine.Experimental.AI/NavMeshQuery.cs-        private static extern PathQueryStatus UpdateFindPath(IntPtr navMeshQuery, int iterations, out int iterationsPerformed);
UnityEngine.AIModule/UnityEngine.Experimental.AI/NavMeshQuery.cs-        private static extern PathQueryStatus EndFindPath(IntPtr navMeshQuery, out int pathSize);
UnityEngine.AIModule/UnityEngine.Experimental.AI/NavMeshQuery.cs-        private unsafe static extern int GetPathResult(IntPtr navMeshQuery, void* path, int maxPath);
UnityEngine.AIModule/UnityEngine.Experimental.AI/NavMeshQuery.cs-        private static bool IsValidPolygon(IntPtr navMeshQuery, PolygonId polygon)
UnityEngine.AIModule/UnityEngine.Experimental.AI/NavMeshQuery.cs-        private static int GetAgentTypeIdForPolygon(IntPtr navMeshQuery, PolygonId polygon)
UnityEngine.AIModule/UnityEngine.Experimental.AI/NavMeshQuery.cs-        private static bool IsPositionInPolygon(IntPtr navMeshQuery, Vector3 position, PolygonId polygon)
UnityEngine.AIModule/UnityEngine.Experimental.AI/NavMeshQuery.cs-        private static PathQueryStatus GetClosestPointOnPoly(IntPtr navMeshQuery, PolygonId polygon, Vector3 position, out Vector3 nearest)
UnityEngine.AIModule/UnityEngine.Experimental.AI/NavMeshQuery.cs-        private static NavMeshLocation MapLocation(IntPtr navMeshQuery, Vector3 position, Vector3 extents, int agentTypeID, int areaMask = -1)
UnityEngine.AIModule/UnityEngine.Experimental.AI/NavMeshQuery.cs-        private unsafe static extern void MoveLocations(IntPtr navMeshQuery, void* locations, void* targets, void* areaMasks, int count);
UnityEngine.AIModule/UnityEngine.Experimental.AI/NavMeshQuery.cs-        private unsafe static extern void MoveLocationsInSameAreas(IntPtr navMeshQuery, void* locations, void* targets, int count, int areaMask);
UnityEngine.AIModule/UnityEngine.Experimental.AI/NavMeshQuery.cs-        private static NavMeshLocation MoveLocation(IntPtr navMeshQuery, NavMeshLocation location, Vector3 target, int areaMask)
UnityEngine.AIModule/UnityEngine.Experimental.AI/NavMeshQuery.cs-        private static bool GetPortalPoints(IntPtr navMeshQuery, PolygonId polygon, PolygonId neighbourPolygon, out Vector3 left, out Vector3 right)
UnityEngine.AIModule/UnityEngine.Experimental.AI/NavMeshQuery.cs-        private static Matrix4x4 PolygonLocalToWorldMatrix(IntPtr navMeshQuery, PolygonId polygon)
UnityEngine.AIModule/UnityEngine.Experimental.AI/NavMeshQuery.cs-        private static Matrix4x4 PolygonWorldToLocalMatrix(IntPtr navMeshQuery, PolygonId polygon)
UnityEngine.AIModule/UnityEngine.Experimental.AI/NavMeshQuery.cs-        private static NavMeshPolyTypes GetPolygonType(IntPtr navMeshQuery, PolygonId polygon)
UnityEngine.UnityAnalyticsModule/UnityEngine.Analytics/UnityAnalyticsHandler.cs-        internal static extern void Internal_Destroy(IntPtr ptr);
UnityEngine.UnityAnalyticsModule/UnityEngine.Analytics/CustomEventData.cs-        internal static extern void Internal_Destroy(IntPtr ptr);
UnityEngine.AnimationModule/UnityEngine.Animations/AnimatorControllerPlayable.cs-        private static extern int StringToHash(string name);
UnityEngine.UnityConnectModule/UnityEngine/RemoteConfigSettings.cs-        internal static extern void Internal_Destroy(IntPtr ptr);
UnityEngine.GameCenterModule/UnityEngine.SocialPlatforms.GameCenter/GcLeaderboard.cs-        private static extern void GcLeaderboard_Dispose(IntPtr leaderboard);
UnityEngine.IMGUIModule/UnityEngine/ObjectGUIState.cs-        private static extern void Internal_Destroy(IntPtr ptr);
UnityEngine.UIElementsModule/UnityEngine.CSSLayout/Native.cs-        private static extern void CSSNodeFreeInternal(IntPtr cssNode);
UnityEngine.UnityWebRequestModule/UnityEngine.Networking/DownloadHandler.cs-        private extern void Release();
UnityEngine.UnityWebRequestModule/UnityEngine.Networking/CertificateHandler.cs-        private extern void Release();
UnityEngine.UnityWebRequestModule/UnityEngine.Networking/UploadHandler.cs-        private extern void Release();
UnityEngine.UnityWebRequestModule/UnityEngine.Networking/UnityWebRequest.cs-        private static extern string GetWebErrorString(UnityWebRequest.UnityWebRequestError err);
UnityEngine.UnityWebRequestModule/UnityEngine.Networking/UnityWebRequest.cs-        private extern void Release();
UnityEngine.UnityWebRequestModule/UnityEngine.Networking/UnityWebRequest.cs-        public extern void Abort();
UnityEngine.ARModule/UnityEngine.XR.Tango/MeshReconstructionServer.cs-        internal static extern void DestroyThreaded(IntPtr server);
UnityEngine.AnimationModule/UnityEngine/Animator.cs-        public static extern int StringToHash(string name);
UnityEngine.AudioModule/UnityEngine.Experimental.Audio/AudioSampleProvider.cs-        private static extern void InternalSetScriptingPtr(uint providerId, AudioSampleProvider provider);
UnityEngine.AudioModule/UnityEngine.Experimental.Audio/AudioSampleProvider.cs-        private static extern bool InternalIsValid(uint providerId);
UnityEngine.AudioModule/UnityEngine.Experimental.Audio/AudioSampleProvider.cs-        private static extern uint InternalGetMaxSampleFrameCount(uint providerId);
UnityEngine.AudioModule/UnityEngine.Experimental.Audio/AudioSampleProvider.cs-        private static extern uint InternalGetAvailableSampleFrameCount(uint providerId);
UnityEngine.AudioModule/UnityEngine.Experimental.Audio/AudioSampleProvider.cs-        private static extern uint InternalGetFreeSampleFrameCount(uint providerId);
UnityEngine.AudioModule/UnityEngine.Experimental.Audio/AudioSampleProvider.cs-        private static extern uint InternalGetFreeSampleFrameCountLowThreshold(uint providerId);
UnityEngine.AudioModule/UnityEngine.Experimental.Audio/AudioSampleProvider.cs-        private static extern void InternalSetFreeSampleFrameCountLowThreshold(uint providerId, uint sampleFrameCount);
UnityEngine.AudioModule/UnityEngine.Experimental.Audio/AudioSampleProvider.cs-        private static extern bool InternalGetEnableSampleFramesAvailableEvents(uint providerId);
UnityEngine.AudioModule/UnityEngine.Experimental.Audio/AudioSampleProvider.cs-        private static extern void InternalSetEnableSampleFramesAvailableEvents(uint providerId, bool enable);
UnityEngine.AudioModule/UnityEngine.Experimental.Audio/AudioSampleProvider.cs-        private static extern bool InternalGetEnableSilencePadding(uint id);
UnityEngine.AudioModule/UnityEngine.Experimental.Audio/AudioSampleProvider.cs-        private static extern void InternalSetEnableSilencePadding(uint id, bool enabled);
UnityEngine.AudioModule/UnityEngine.Experimental.Audio/AudioSampleProvider.cs-        private static extern IntPtr InternalGetConsumeSampleFramesNativeFunctionPtr();

注意,其中一些是私有的,因此只能通过公共API间接使用。例如,调用Debug。日志来间接调用私有的UnityLogWriter.WriteStringToUnityLogImpl。

在纯c#中还实现了一些Unity API函数。例如,Vector3。颜色。Lerp不会对引擎的本机c++代码进行任何调用。这些函数都可以在作业中使用。如果这些函数只调用线程安全的本机代码函数,也可以调用它们,如上所述和列出的。

定义一个Job

第一步是定义Job。Job应该做少量定义良好的工作,类似于函数。首先,定义一个结构体:

public struct ApplyVelocityJob
{
}

接下来,决定做什么样的Job。目前有三种工作可供选择:

  • IJob: 执行一次的简单作业
  • IJobParallelFor: 为一系列值执行的作业
  • IJobParallelForTransform: 能访问transforms的作业

让我们从简单的IJob开始,并实现它:

public struct ApplyVelocityJob : IJob
{
    public void Execute()
    {
    }
}

现在我们需要定义Job的输入和输出。为此,我们将字段添加到结构中,而不是将参数添加到其执行方法中。该Job将根据给定的运行时间范围内的相应速度修改对象的位置。让我们添加一些字段:

public struct ApplyVelocityJob : IJob
{
    public float ElapsedTime;
    public NativeArray<Vector3> Positions;
    public NativeArray<Vector3> Velocities;
 
    public void Execute()
    {
    }
}

为了实现更大的优化和错误检查,我们可以选择向这些“参数”字段添加[ReadOnly]和[WriteOnly]属性:

public struct ApplyVelocityJob : IJob
{
    [ReadOnly]
    public float ElapsedTime;
 
    public NativeArray<Vector3> Positions;
 
    [ReadOnly]
    public NativeArray<Vector3> Velocities;
 
    public void Execute()
    {
    }
}

最后一步是通过填写执行方法来实际实现Job的逻辑:

public struct ApplyVelocityJob : IJob
{
    [ReadOnly]
    public float ElapsedTime;
 
    public NativeArray<Vector3> Positions;
 
    [ReadOnly]
    public NativeArray<Vector3> Velocities;
 
    public void Execute()
    {
        for (int i = 0; i < Positions.Length; ++i)
        {
            Positions[i] += Velocities[i] * ElapsedTime;
        }
    }
}

就是这样。然而,我们可以通过使其实现IJobParallelFor接口轻松实现并行化作业。作业系统将对数组的每个元素运行一次Execute,而不是对整个数组运行一次Execute。每次调用执行时,它可能会使用不同的CPU内核,这意味着只需进行一些简单的调整,作业就可以利用系统中的所有内核。让我们看看如何做:

public struct ApplyVelocityParallelJob : IJobParallelFor
{
    [ReadOnly]
    public float ElapsedTime;
 
    public NativeArray<Vector3> Positions;
 
    [ReadOnly]
    public NativeArray<Vector3> Velocities;
 
    public void Execute(int index)
    {
        Positions[index] += Velocities[index] * ElapsedTime;
    }
}

由于for循环实际上已经转移到作业系统,因此编写并行版本可以说更容易,因为我们不再需要编写循环了。最后,如果我们想在作业中修改游戏对象的Transform,那么我们需要实现IJobParallelForTransform。让我们修改这个作业,使它不只是在NativeArray中使用一个Vector3,而是实际改变游戏对象的位置:

public struct ApplyVelocityTransformJob : IJobParallelForTransform
{
    [ReadOnly]
    public float ElapsedTime;
 
    [ReadOnly]
    public NativeArray<Vector3> Velocities;
 
    public void Execute(int index, TransformAccess transform)
    {
        transform.localPosition += Velocities[index] * ElapsedTime;
    }
}

创建一个Job

创建作业就像创建其他结构一样简单。只需调用new并填写所有字段。初始化器语法可以很方便地做到这一点:

ApplyVelocityJob job = new ApplyVelocityJob
{
    ElapsedTime = Time.time,
    Positions = positions,
    Velocities = velocities
};
 
ApplyVelocityParallelJob parallelJob = new ApplyVelocityParallelJob
{
    ElapsedTime = Time.time,
    Positions = positions,
    Velocities = velocities
};
 
ApplyVelocityTransformJob transformJob = new ApplyVelocityTransformJob
{
    ElapsedTime = Time.time,
    Velocities = velocities
};

开始一个Job

Jobs可以从几个方面开始。让我们从IJob开始:

JobHandle jobHandle = job.Schedule();

调度作业会告诉作业系统它已经准备好开始了。我们得到一个JobHandle,我们可以用它来指代未来的作业。例如,如果我们想开始一个依赖于另一个任务完成的任务,我们可以将其传递到进度表中。例如,要运行同一个作业两次,我们可以让第二个作业在第一个作业完成后运行:

// Schedule with no dependencies
JobHandle jobHandle1 = job.Schedule();
 
// Schedule with dependencies
JobHandle jobHandle2 = job.Schedule(jobHandle1);

现在让我们尝试安排一个IJobParallelFor。为此,我们需要提供一些额外的参数。第一个是循环应该运行的迭代次数。第二个是在单个核心上的“批处理”中运行的迭代次数。这不是一个正确的值,可以对小作业调得更大,对大作业调得更小。以下是日程调用的工作方式:

// Schedule with no dependencies
JobHandle jobHandle1 = parallelJob.Schedule(positions.Length, 32);
 
// Schedule with dependencies
JobHandle jobHandle2 = parallelJob.Schedule(positions.Length, 32, jobHandle1);

最后是IJobParallelForTransform,它接受TransformAccessArray参数。这个参数封装了一个Transform对象的托管数组,需要通过一个Dispose调用手动释放,就像NativeArray一样。以下是如何创建和安排工作的方法:

// Get or create a managed array of Transform objects
Transform[] transforms = new [] { transform };
 
// Wrap the managed array in a TransformAccessArray
TransformAccessArray accessArray = new TransformAccessArray(transforms);
 
// Schedule with no dependencies
JobHandle jobHandle1 = transformJob.Schedule(accessArray);
 
// Schedule with dependencies
JobHandle jobHandle2 = transformJob.Schedule(accessArray, jobHandle1);

在安排所有作业之后还有最后一步,或者在安排下一个作业之前主线程上有大量工作要做。我们需要告诉作业系统,我们已经准备好让它开始执行预定的作业:

JobHandle.ScheduleBatchedJobs();

工作通常都有日程安排和任务处理。ScheduleBatchedJobs,但是IJob和IJobParallelFor也可以同步运行,以便在继续之前立即完成。这不是IJobParallelForTransform的选项。在执行此操作时,我们使用不同的参数调用Run,并且不涉及依赖项。以下是如何以这种方式管理工作:

job.Run();
 
parallelJob.Run(positions.Length);

未完待续

发布了97 篇原创文章 · 获赞 21 · 访问量 28万+

猜你喜欢

转载自blog.csdn.net/sun124608666/article/details/101460013
job