【Unity / Thread】Unity 中子线程回调主线程的方法

Unity 引擎的游戏逻辑和渲染,都是在主线程中进行的,和大多数其他游戏引擎一样,Unity 也是一个单线程的引擎。这与常规开发的思路可能不太一样,通常认为多线程能够很大程度上提升程序的性能效率,何况是游戏这种需要大量资源和大量计算的复杂程序。其实不然,单线程能为游戏引擎开发和游戏开发带来很多好处,未经过精心设计的多线程也可能会为中后期开发带来灾难性的后果。但这不是本文要讨论的重点,感兴趣的话可以自行搜索游戏引擎设计和多线程相关的资料。

综合考虑众多因素,大量游戏引擎都采用了单线程的设计,但这也带来了一些问题,Unity 提供的大多数非值类型对象都是不允许在主线程之外访问的,但很多时候应该使用或者无法避免使用多线程,同时子线程也必须要和主线程发生交互,例如:

  • 1.相对独立且运算复杂的独立模块,比如网络通讯。游戏与服务端的数据交互,解析结果最终需要反馈到游戏逻辑中呈现给玩家。
  • 2.支付、广告、统计分析等众多第三方SDK的逻辑代码会在各自的线程上执行,但SDK各种功能的状态和结果是需要通知游戏主线程的。

目前我使用的方法是利用 C# 的委托功能,将子线程回调主线程的函数以事件形式注册到委托上,主线程使用一个管理类,按一定频率去查询尚未执行的子线程事件,并依次执行。

定义待执行的子线程事件队列:

        private static bool NoUpdate = true;
        private static List<Action> UpdateQueue = new List<Action>();
        private static List<Action> UpdateRunQueue = new List<Action>();

添加待执行子线程事件的接口:

        public static void ExecuteUpdate(Action action)
        {
            lock (UpdateQueue)
            {
                UpdateQueue.Add(action);
                NoUpdate = false;
            }
        }

主线程上的轮询方法:

        private void Update()
        {
            lock (UpdateQueue)
            {
                if (NoUpdate) return;
                UpdateRunQueue.AddRange(UpdateQueue);
                UpdateQueue.Clear();
                NoUpdate = true;
                for (var i = 0; i < UpdateRunQueue.Count; i++)
                {
                    var action = UpdateRunQueue[i];
                    if (action == null) continue;
                    action();
                }
                UpdateRunQueue.Clear();
            }
        }

可以将这些方法都写在一个 MonoBehaviour 的单例类中,子线程通过单例静态接口访问到这个管理类进行事件添加,主线程通过 Unity 自身生命周期的 Update() 去轮询执行子线程的回调事件,对以上代码稍加复制和修改,就可以实现 ExecuteLateUpdateExecuteFixedUpdate 接口,在必要的时候将子线程事件安排到一帧的末尾或者在物理更新中执行。

这样做也有一些问题,所有的子线程回调在主线程上的执行都不是立即的,根据轮询时机的不同,子线程发起的回调事件的执行会有一定的延时,如果是附加到 Update 中执行则最多会延迟一帧的时间,但帧数受到设备性能的影响,延时时间也会有波动,如果附加到 FixedUpdate 执行,延时问题会得到所改善。

通过子线程在主线程上创建协程和延时执行:

        public static void ExecuteDelay(Action action, float delayTime, bool timeScale = true)
        {
            ExecuteCoroutine(DelayCoroutine(action, delayTime, timeScale));
        }

        private static IEnumerator DelayCoroutine(Action action, float delayTime, bool timeScale = true)
        {
            if (timeScale)
            {
                yield return new WaitForSeconds(delayTime);
            }
            else
            {
                yield return new WaitForSecondsRealtime(delayTime);
            }
            action();
        }
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Unity中,由于线程是单线程的,不能直接在其他线程中直接调用线程中的方法。这是因为Unity的API大多数是不是线程安全的,只能在线程中被调用。 然而,Unity提供了一种方式来在其他线程中调用线程中的方法,即使用线程调度器。线程调度器允许你将任务(方法)添加到线程的队列中,在线程的适当时机执行这些任务。 以下是一个使用线程调度器的示例代码: ```csharp using UnityEngine; using System.Threading; public class MainThreadDispatcher : MonoBehaviour { private static MainThreadDispatcher instance; private Thread mainThread; private static readonly object lockObject = new object(); private static bool isQuitting = false; private ConcurrentQueue<System.Action> actionQueue = new ConcurrentQueue<System.Action>(); private void Awake() { if (instance != null) { Destroy(this); return; } instance = this; DontDestroyOnLoad(this); mainThread = Thread.CurrentThread; } private void OnDestroy() { instance = null; isQuitting = true; } public static void RunOnMainThread(System.Action action) { if (isQuitting) return; lock (lockObject) { instance.actionQueue.Enqueue(action); } } private void Update() { lock (lockObject) { while (!actionQueue.IsEmpty) { if (actionQueue.TryDequeue(out System.Action action)) { action.Invoke(); } } } } } ``` 在需要在其他线程中调用线程方法的地方,可以使用以下代码: ```csharp MainThreadDispatcher.RunOnMainThread(() => { // 在线程中执行的方法 }); ``` 这样,通过使用线程调度器,你可以在其他线程中将需要在线程执行的方法添加到线程的队列中,确保它们在适当的时机被执行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ls9512

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值