Unity3D中的线程与协程

线程 

  Unity3D是以生命周期主线程循环进行游戏开发。

  Unity3D中的子线程无法运行Unity SDK(开发者工具包,软件包、软件框架)跟API(应用程序编程接口,函数库)。

  限制原因:大多数游戏引擎都是主循环结构,游戏中逻辑更新和画面更新的时间点要求有确定性,必须按照帧序列严格保持同步,否则就会出现游戏中的对象不同步的现象。虽然多线程也能保证这个效果,但是引用多线程,会加大同步处理的难度与游戏的不稳定性。

  但是多线程也是有好处的,如果不是画面更新,也不是常规的逻辑更新(指包括AI、物理碰撞、角色控制这些),而是一些其他后台任务,比如大量耗时的数据计算、网络请求、复杂密集的I/O操作,则可以将这个独立出来做成一个工作线程,这需要写Unity游戏的Native扩展。

协程

  对于Unity3D,它是生命周期主线程循环的设计,它更倾向于使用Time slicing(时间分片)的Coroutine(协程)去完成异步任务,融合到生命周期中。

  线程是操作系统级别的概念,现代操作系统都支持并实现线程,线程的调度对应用开发者是透明的,开发者无法预期某线程在何时被调度执行。基于此,一般那种随机出现的BUG,多与线程调度相关。

  而协程Coroutine是编译器级别的,本质是一个线程时间片去执行代码段。它通过相关的代码使得代码段能够实现分段式的执行,显式调用yield函数后才被挂起,重新开始的地方是yield挂起的位置,每一次执行协程会跑到下一个yield语句。协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。

  在Unity3D中,协程是可自行停止运行 (yield),直到给定的 YieldInstruction 结束再继续运行的函数。协程 (Coroutines) 的不同用途: ·

       (1) yield return null - 这一帧到此暂停,下一帧再从暂停处继续,常用于循环中。

       (2) yield return new WaitForEndOfFrame - 等到这一帧的cameras和GUI渲染结束后再从此处继续,即等到这帧的末尾再往下运行。这行之后的代码还是在当前帧运行,是在下一帧开始前执行,跟return null很相似。

       (3) yield return new WaitForFixedUpdate - 在下一次执行FixedUpdate的时候继续执行这段代码,即等一次物理引擎的更新。

       (4) yield return new WaitForSeconds(3.0f) - 等待3秒,然后继续从此处开始,常用于做定时器。

       (5) yield return WWW - 等待直至异步下载完成。

       (6) yield return StartCoroutine(methodName) - 等待另一个协程执行完。这是把协程串联起来的关键,常用于让多个协程按顺序逐个运行。

  (7)yield break - 直接跳出协程,对某些判定失败必须跳出的时候,比如加载AssetBundle的时候,WWW失败了,后边加载bundle没有必要了,这时候可以yield break跳出。

  值得注意的是 WaitForSeconds()受Time.timeScale影响,当Time.timeScale = 0f 时,yield return new WaitForSecond(x) 将不会满足。

  以下为Unity3D的生命周期循环图

c#代码示例

Unity3D使用协程常需要用到辅助类Stopwatch,提供一组可用于准确地测量运行时间的方法和属性。

private Stopwatch frameStopwatch;//用来记录上一帧结束到现在所用的时间
private float targetFrameDuration;//自定义的每帧持续时间,防止协程过度消耗线程时间片
private void Awake()
{
    frameStopwatch = new Stopwatch();
}

void Update()
{
    //计算每一帧所用的时间Start()之后Elapsed会一直增加,Stop()之后Elapsed的值就不变
    frameStopwatch.Stop();
    frameStopwatch.Reset();
    frameStopwatch.Start();

  if(ChunkUpdateList.Count > 0)
  {
    StartCoroutine(ProcessChunkQueueLoop());//启动协程处理ChunkUpdateList
  } }

private IEnumerator ProcessChunkQueueLoop()
{
    while (ChunkUpdateList.Count > 0)
    {
        ProcessChunkUpdateList();//每次处理ChunkUpdateList中的一个数据
        if (frameStopwatch.Elapsed.TotalSeconds >= targetFrameDuration)//这一帧已经运行的时间frameStopwatch.Elapsed.TotalSeconds已经超过自定义每帧的时间targetFrameDuration,则挂起直到这一帧结束再运行
        {
            yield return new WaitForEndOfFrame();
        }
    }
}

引用:

  (1)游戏引擎Unity中的单线程与多线程

其他:

  (1)游戏主循环

  (2)3D引擎多线程:渲染与逻辑分离

  

原文地址:https://www.cnblogs.com/DonYao/p/8571981.html