Unity C# 脚本顺序执行等待方案及性能优化求助
解决协程顺序等待的性能问题
你的原代码性能下降的核心原因是:每次调用TriggerSomething()都会启动一个新的协程,这个协程会每帧循环检查_Something是否为空。如果短时间内多次触发这个方法,就会有多个等待协程同时运行,每帧都执行检查逻辑,累积起来就会消耗额外的性能。
下面是几种更高效的替代方案,都能在void方法里实现“等待前一个任务完成再启动新任务”的需求:
方案一:任务队列 + 单协程处理(推荐多调用场景)
维护一个任务队列,用单个协程依次处理所有排队的任务。这样不管调用多少次触发方法,都只会有一个协程在运行,彻底避免多协程等待的性能浪费。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Example : MonoBehaviour { // 存储待执行的任务队列 private Queue<Func<IEnumerator>> _taskQueue = new Queue<Func<IEnumerator>>(); // 标记是否正在处理任务 private bool _isProcessingTasks = false; void TriggerSomething() { // 将目标任务加入队列 _taskQueue.Enqueue(Something); // 如果当前没有处理任务,启动处理协程 if (!_isProcessingTasks) { StartCoroutine(ProcessTaskQueue()); } } // 专门处理任务队列的协程 IEnumerator ProcessTaskQueue() { _isProcessingTasks = true; // 循环处理队列中的所有任务 while (_taskQueue.Count > 0) { var currentTask = _taskQueue.Dequeue(); // 等待当前任务执行完成,再处理下一个 yield return StartCoroutine(currentTask()); } _isProcessingTasks = false; } // 你的实际任务逻辑 IEnumerator Something() { yield return null; // do something Debug.Log("任务执行完成"); } }
这个方案的优势是:
- 自动按调用顺序执行任务,不会出现任务插队
- 同一时间只有一个协程在运行,性能开销极低
- 支持无限次触发,任务会自动排队等待
方案二:直接等待协程完成(适合单次等待场景)
如果不需要排队,只是每次触发时等待当前任务完成再启动新任务,可以直接利用Unity协程的特性——协程可以等待另一个协程完成,不需要每帧检查变量。
using System.Collections; using UnityEngine; public class Example : MonoBehaviour { private IEnumerator _currentTask = null; void TriggerSomething() { if (_currentTask == null) { // 当前无任务,直接启动 _currentTask = RunSomethingWithCallback(); StartCoroutine(_currentTask); } else { // 当前有任务,启动等待协程,等完成后再启动新任务 StartCoroutine(WaitForCurrentTaskThenStart()); } } IEnumerator WaitForCurrentTaskThenStart() { // 直接等待当前协程完成,无需每帧检查 yield return _currentTask; _currentTask = RunSomethingWithCallback(); StartCoroutine(_currentTask); } IEnumerator RunSomethingWithCallback() { yield return null; // do something Debug.Log("任务执行完成"); // 任务完成后清空标记 _currentTask = null; } }
这个方案的优势是:
- 代码更简洁,避免了队列的维护
- 等待逻辑更高效,只有在需要时才启动等待协程
方案三:使用Async/Await(现代代码风格)
如果你的Unity版本在2017.1及以上,可以用async/await语法来简化代码,实现类似协程的顺序等待,同时代码可读性更高。
using System.Threading.Tasks; using UnityEngine; public class Example : MonoBehaviour { private Task _currentTask = null; // 注意:async void 仅适合在事件/触发方法中使用,避免异常无法捕获 async void TriggerSomething() { // 如果当前有任务,等待它完成 if (_currentTask != null) { await _currentTask; } // 启动新任务 _currentTask = SomethingAsync(); await _currentTask; _currentTask = null; } async Task SomethingAsync() { // 对应协程的 yield return null,等待一帧 await Task.Yield(); // do something Debug.Log("任务执行完成"); } }
这个方案的优势是:
- 代码逻辑更直观,和普通的异步代码写法一致
- 无需手动管理协程的启动和等待,Unity会自动处理主线程上下文
内容的提问来源于stack exchange,提问作者Burak Bekeç
相关产品推荐
相关产品推荐

