Unity中 JobSystem使用整理

Unity 的JobSystem允许创建多线程代码,以便应用程序可以使用所有可用的 CPU 内核来执行代码,这提供了更高的性能,因为您的应用程序可以更高效地使用运行它的所有 CPU 内核的容量,而不是在一个 CPU 内核上运行所有代码。
可以单独使用JobSystem,为了提高性能,可以和Burst 编译器一块使用,Burst 编译器改进了代码生成,从而提高了移动设备的性能并减少了电池消耗。
还可以将JobSystem与 Unity 的实体组件系统结合使用,以创建高性能的面向数据的代码。

概念:

单线程:一次执行一条指令,并且一次产生一个结果。加载和完成程序的时间取决于 CPU 需要完成的工作量。
多线程:利用了 CPU 在多个核心上同时处理多个线程的能力。这种情况下不是逐个执行任务或指令,而是同时运行任务或指令
什么是多线程? - Unity 手册
JobSystem:通过创建Job而不是线程来管理多线程代码,将创建的Job放入Job队列中等待执行,工作线程从Job队列中获取并执行Job,用依赖关系来确保Job用适当的顺序执行。
Job完成一项特定任务的一个小工作单位,会接收参数并对数据进行操作,可以是独立的,也可依赖其他作业完成之后才能运行。
Job依赖关系: 如果jobA依赖于jobB,系统将确保jobA不会在jobB完成之前开始执行。
什么是作业系统? - Unity 手册

优势:

1.多线程并行计算:利用多核CPU提升性能。
​2.无GC分配:使用 Native 容器避免托管堆分配。
​3.Burst 编译优化:生成高效原生代码(性能提升5-10倍)。

使用:

1.安装

通过Package Manager添加

通过Git URL地址搜索Job System包  
com.unity.jobs:Job System 的核心功能包。

搜索到Jobs包后点击右下角Import

2.JobSystem使用入门:

1.NativeContainer:

1.核心容器:NativeArray<T> NativeSlice<T> 
2.特殊数据结构容器:NativeBitArray
3.动态容器:​​NativeList<T>、NativeQueue<T>
4.并行安全容器:
NativeParallelHashMap<TKey,TValue>
NativeParallelHashSet<T>
​NativeParallelMultiHashMap<TKey,TValue>

5.迭代器:
​NativeParallelMultiHashMapIterator<T>

2.第一个Job例子:

1.创建Job:
1.创建实现 IJob 的结构。
2.添加该作业使用的成员变量(为 blittable 类型和 NativeContainer 类型之一)。
3.在结构中创建一个名为 Execute 的方法,并在其中实现该作业。

  public struct MyJob : IJob
    {
        public float a;
        public float b;
        public NativeArray<Vector3> result;

        public void Execute()
        {
            result[0] = new Vector3(a,0,b);
        }
    }

2.运行Job:
1.实例化该作业。
2.填充作业的数据。
3.调用 Schedule 方法,会将该作业放入作业队列中,以便在适当的时间执行。

    NativeArray<Vector3> result;
    JobHandle handle;

    void Update()
    {
        result = new NativeArray<Vector3>(1, Allocator.TempJob);

        MyJob jobData = new MyJob
        {
            a = 10,
            b = 10,
            result = result
        };
        handle = jobData.Schedule();        
    }

    private void LateUpdate()
    {
        handle.Complete();
        Debug.Log(result[0]);
        result.Dispose();
    }

结果:

3.JobHandle 和依赖项:

调用Job的 Schedule 方法时,将返回 JobHandle。可以在代码中使用 JobHandle 作为其他Job的依赖项,一个Job可以依赖于多个Job,在其所依赖的Job完成之前,JobSystem不会运行此Job。

JobHandle firstJobHandle = firstJob.Schedule();
secondJob.Schedule(firstJobHandle);

合并依赖项:使用JobHandle.CombineDependencies合并多个依赖项。

NativeArray<JobHandle> handles = new NativeArray<JobHandle>(numJobs, Allocator.TempJob);
JobHandle jh = JobHandle.CombineDependencies(handles);

多个依赖项执行例子

1.在定义新的Job:

    public struct MyJob : IJob
    {
        public float a;
        public float b;
        public NativeArray<Vector3> result;

        public void Execute()
        {
            for (int i = 0; i< 100; i++)
            {
                a += 1;
                b += 1;
            }
            result[0] = new Vector3(a + 100, 0, b + 100);
        }
    }

    // 将一个值加一的作业
    public struct AddOneJob : IJob
    {
        public NativeArray<Vector3> result;

        public void Execute()
        {
            result[0] = result[0] + Vector3.one;
        }
    }

2.调度Job: 

    NativeArray<Vector3> result;
    JobHandle handle;
    JobHandle firstHandle;

    void Update()
    {
        result = new NativeArray<Vector3>(1, Allocator.TempJob);

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

        firstHandle = jobData.Schedule();

        AddOneJob incJobData = new AddOneJob();
        incJobData.result = result;

        // 调度作业 #2
        handle = incJobData.Schedule(firstHandle);
    }

    private void LateUpdate()
    {
        handle.Complete();
        Debug.Log(result[0]);
        result.Dispose();   
    }

结果:
 

4.并行Job:

在调度 ParallelFor 作业时,必须指定要拆分的 NativeArray 数据源的长度(表示有多少个 Execute 方法)。如果结构中有多个 NativeArray,无法确定将哪个用作数据源。
C# JobSystem将Job分成多个批次以便在多个核心之间分配任务,每个批次包含一小部分 Execute 方法,然后,JobSystem在 Unity 的Native Job System中为每个 CPU 内核安排一个Job,并将该Native Job传递给要完成的批次,如下图所示:

1.ParallelFor Job
定义一个并行Job:

public struct MyParallelJob : IJobParallelFor
{
    [ReadOnly]
    public NativeArray<float> a;
    [ReadOnly]
    public NativeArray<float> b;
    public NativeArray<float> result;

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

调度Job:

public class ParallelJobTest : MonoBehaviour
{
    NativeArray<float> a;
    NativeArray<float> b;
    NativeArray<float> result;
    JobHandle handle;

    // Update is called once per frame
    void Update()
    {
        a = new NativeArray<float>(3, Allocator.TempJob)
        {
            [0] = 1,
            [1] = 2,
            [2] = 3
        };
        b = new NativeArray<float>(3, Allocator.TempJob)
        {
            [0] = 100,
            [1] = 200,
            [2] = 111
        };
        result = new NativeArray<float>(3, Allocator.TempJob);
        MyParallelJob jobData = new MyParallelJob();
        jobData.a = a;
        jobData.b = b;
        jobData.result = result;
        // Schedule the job with one Execute per index in the results array and only 1 item per processing batch
        handle = jobData.Schedule(result.Length, 1);
        // Wait for the job to complete
        handle.Complete();
        Debug.Log($"{result[0]},{result[1]},{result[2]}");
        // Free the memory allocated by the arrays
        a.Dispose();
        b.Dispose();
        result.Dispose();
    }
}

 结果:

2.ParallelForTransform Job
ParallelForTransform Job允许对传递到作业中的所有转换的每个位置、旋转和缩放执行相同的独立作。

定义一个ParallelForTransform Job:

public struct VelocityJob : IJobParallelForTransform
{
    [ReadOnly]
    public NativeArray<Vector3> velocity;
    public float deltaTime;

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

调度Job:

public class ParallelForTransformTest : MonoBehaviour
{
    [SerializeField] public Transform[] m_Transforms;
    TransformAccessArray m_AccessArray;
    // Start is called before the first frame update
    void Start()
    {
        m_AccessArray = new TransformAccessArray(m_Transforms);
    }

    // Update is called once per frame
    void Update()
    {
        var velocity = new NativeArray<Vector3>(m_Transforms.Length, Allocator.Persistent);

        for (var i = 0; i < velocity.Length; ++i)
            velocity[i] = new Vector3(0f, 10f, 0f);

        var job = new VelocityJob()
        {
            deltaTime = Time.deltaTime,
            velocity = velocity
        };

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

        Debug.Log(m_Transforms[0].position);

        velocity.Dispose();
    }

    private void OnDestroy()
    {
        m_AccessArray.Dispose();
    }
}

结果: 

3.注意事项:

1.不要从Job访问静态数据
2.不要更新 NativeContainer 内容
3.调用 JobHandle.Complete 以重新获得所有权
4.在适当时间使用 Schedule 和 Complete 方法
5.将 NativeContainer 类型标记为只读
6.不要在Job中分配托管内存

C# 作业系统提示和故障排除 - Unity 手册

参考链接:

什么是作业系统? - Unity 手册

ArtStation - Unity Job System in Practice. How we increased FPS from 15 to 70 in our game

<think>好的,我现在需要帮助用户解决关于贝加莱(B&R Beijer)的OPC UA与Unity引擎集成的问题。首先,我得先理解用户的需求。用户可能是在开发一个工业仿真或监控系统,想要将Unity作为前端展示,而贝加莱的PLC通过OPC UA提供数据。需要找到两者之间通信的方法。 首先,我应该回忆一下OPC UA的基本知识。OPC UA是一种用于工业自动化的通信协议,支持跨平台的数据传输,常用于设备间的数据交换。Unity是一个游戏引擎,但也被广泛用于工业仿真、数字孪生等可视化项目。要让两者交互,通常需要通过某种中间件或SDK来实现OPC UA的客户端功能。 接下来,我需要考虑贝加莱的OPC UA支持情况。贝加莱的控制器通常支持OPC UA服务器功能,所以Unity需要作为客户端来连接。而Unity本身可能没有内置的OPC UA库,所以需要寻找第三方插件或自己实现。用户可能希望知道有哪些可用的工具或方法。 可能的解决方案包括使用C#的OPC UA库,例如UA-.NETStandard库,然后在Unity使用C#脚本调用该库。不过Unity支持的.NET版本可能有限,需要检查兼容性。另外,是否有现成的Unity插件可用,比如OPC UA Unity插件,或者通过REST API或WebSocket进行中转。 还需要考虑实时性、数据量以及安全性。OPC UA通常要求加密和身份验证,这些在集成时也需要处理。另外,如果使用中间件,比如Node-RED或Python脚本,可能会增加复杂性,但可以简化Unity端的开发。 然后,我应该整理步骤:首先在贝加莱PLC配置OPC UA服务器,然后在Unity项目中集成OPC UA客户端,可能需要编写C#脚本或使用插件。需要详细说明每个步骤的具体操作,可能还需要代码示例。 同时,需要注意引用相关的资源,比如贝加莱的官方文档、OPC基金会提供的库,或者第三方插件的链接。如果有相关的教程或案例,也应该提及,供用户参考。 可能用户会遇到的问题包括库的兼容性、连接配置错误、数据订阅问题等,需要给出一些常见问题的解决方法或建议。此外,性能优化也是一个点,比如如何处理高频数据更新而不影响Unity的渲染性能。 最后,生成的相关问题需要围绕集成方法、实时数据通信、安全配置、性能优化等方面展开,帮助用户进一步探索相关问题。</think>### 贝加莱(B&R)OPC UA与Unity的集成方法 要将贝加莱的OPC UA服务器与Unity引擎集成,核心是通过OPC UA协议实现工业设备数据与Unity可视化应用的实时交互。以下是具体步骤和工具建议: #### 1. **配置贝加莱设备的OPC UA服务器** - 贝加莱控制器(如Automation PC或X20系列)默认支持OPC UA服务器功能。需通过Automation Studio配置以下内容: - 启用OPC UA服务,并设置端口号(默认4840)[^1]。 - 定义需要暴露的变量标签(如传感器数据、控制指令)。 - 配置安全策略(如加密方式、用户身份验证)。 #### 2. **在Unity中实现OPC UA客户端** Unity需通过C#脚本调用OPC UA客户端库。推荐以下两种方法: - **方法一:使用UA-.NETStandard库** - OPC基金会提供的开源库`OPC UA .NET Standard`支持跨平台通信[^2]。 - 示例代码: ```csharp using Opc.Ua.Client; public class OPCUA_Client : MonoBehaviour { private Session session; void Start() { var endpoint = "opc.tcp://192.168.1.100:4840"; var config = new ApplicationConfiguration() { /* 配置参数 */ }; var channel = SessionChannel.Create(config, endpoint); session = new Session(channel); // 订阅变量 var subscription = new Subscription(session.DefaultSubscription); subscription.AddMonitoredItem("ns=2;s=Temperature"); } } ``` - **方法二:使用第三方Unity插件** - 插件如[**Unity OPC UA Plugin**](https://assetstore.unity.com/packages/tools/network/opc-ua-client-213919)可简化集成流程,提供可视化配置界面。 #### 3. **数据交互与可视化** - **实时数据更新**:通过订阅(Subscription)模式监听贝加莱设备变量变化,并在Unity中更新3D模型状态(如机械臂运动、仪表盘数值)。 - **控制指令下发**:通过`Write`方法向贝加莱设备发送Unity端的输入指令(如按钮触发控制信号)。 #### 4. **安全与性能优化** - **加密通信**:启用OPC UA的`Sign & Encrypt`模式,防止数据篡改[^3]。 - **异步处理**:使用多线程或Unity的`Job System`避免数据通信阻塞主渲染线程。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值