【Unity】协程 & Async

发布于:2025-08-05 ⋅ 阅读:(18) ⋅ 点赞:(0)

一、协程

协程是 Unity 内置的异步机制,通过 yield 暂停执行,实现任务在多帧中分段执行。与普通函数不同,协程可在执行过程中挂起和恢复,呈现"并发"效果,但本质上仍运行于主线程。若在协程中进行耗时操作,仍会导致主线程阻塞。因此,协程更适合处理异步事件的调度,而非需要多线程计算的任务。

优势:

  • 使用简洁:在需要处理耗时的异步操作(如等待 HTTP 传输、资源加载、文件 I/O)时,官方建议优先考虑协程。
  • Unity 生命周期集成:协程由MonoBehaviour管理,随脚本或对象生命周期自动中止。比如当GameObject被停用或销毁时,其上运行的协程也会停止。
  • 无需线程切换:协程运行在 Unity 主线程,不必担心线程安全,因为代码始终在主线程执行。

劣势:

  • 依赖 MonoBehaviour
  • 无法直接获取返回值:协程返回类型为IEnumerator,无法直接提供结果。通常需通过回调、全局状态或等待协程结束来获取数据,不如Task<T>直接。
  • 错误处理复杂:在协程内不能直接使用try/catch捕获包含yield的代码块中的异常,这使得异常处理更加困难。未处理的异常可能导致游戏逻辑问题被掩盖。
  • 性能与GC开销:尽管单个协程开销小,但大量协程会增加垃圾回收压力并可能影响帧率(如装箱迭代器、WaitForSeconds等对象),尤其是在每帧更新后统一调度时。

使用场景:

需要按照顺序执行、需要暂停、运行比单个帧花费时间更长的操作。

  • 将对象移动到某个位置。
  • 为对象提供要执行的任务列表。
  • 淡入淡出视觉或音频。
  • 等待资源加载:如Resources.LoadAsync/SceneManager.LoadSceneAsync
  • 网络请求封装:先 UnityWebRequest.SendWebRequest(),然后yield return request 等待请求完成,再检查结果。

yield return

Yield 表示该方法是一个迭代器,它将在多个帧上执行,而 return 与常规函数一样,在该点终止执行并将控制权传递回调用方法。

不同之处在于,对于协程,Unity 知道从上次中断的地方继续该方法。yield return 后面的内容将指定 Unity 在继续之前将等待多长时间。

  • yield return null - 等待下一帧
  • yield return new WaitForSeconds(秒数) - 等待一段时间

对于重复延迟,可以创建一个对象优化性能。

WaitForSeconds delay = new WaitForSeconds(1); // 创建对象

    Coroutine coroutine;

    void Start()
    {
        StartCoroutine("MyCoroutine");
    }

    IEnumerator MyCoroutine()
    {
        int i = 100;

        while (i > 0)
        {
            // Do something 100 times
            i--;

            yield return delay; // 使用对象
        }
        // All Done!
    }
  • yield return new WaitForSecondsRealtime(秒数) - 实时等待秒数
  • yield return new WaitUntil(条件)/WaitWhile(条件) - 等待委托
1. 用外部函数
yield return new WaitUntil(IsEmpty);
bool void IsEmpty()
{
    ...
    //返回true或false
}

2. 用lambda表达式
IEnumerator CheckFuel()
{
    yield return new WaitWhile(() => fuel > 0);
    ...
}
  • yield return new WaitForEndOfFrame() - 等待帧结束
    会等到 Unity 渲染完每个 Camera 和 UI 元素,然后再实际显示帧。这方面的典型用途是截取屏幕截图。
  • yield return StartCoroutine()- 等待另一个协程
void Start()
    {
        // 启动协程
        StartCoroutine(MyCoroutine());
    }

    IEnumerator MyCoroutine()
    {
        // 启动等待第二个协程
        yield return StartCoroutine(MyOtherCoroutine());
    }

    IEnumerator MyOtherCoroutine()
    {
        ...
        yield return new WaitForSeconds(1);
    }

结束协程

  • yield break - 在协程完成前结束协程
  • StopCoroutine - 在协程外部停止协程
bool stopCoroutine;
Coroutine runningCoroutine;

    void Start()
    {
        runningCoroutine = StartCoroutine(MyCoroutine());
    }

    void Update()
    {
        if (stopCoroutine == true)
        {
            StopCoroutine(runningCoroutine);
            stopCoroutine = false;
        }
    }

    IEnumerator MyCoroutine()
    {
        // Coroutine stuff...
    }
  • StopAllCoroutines()- 停止MonoBehaviour上的所有协程
    需要从启动它们的行为中调用 。

其他:

二、Async

待续。。。


网站公告

今日签到

点亮在社区的每一天
去签到