You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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ç

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 08:47:38