Unity-MonoBehaviour底层原理
一 丶定义:
协程(Coroutines)是一种比线程更加轻量级的存在。协程完全由程序所控制(在用户态执行),带来的好处是性能大幅度的提升。协程是一个特殊的函数,这个函数可以在某个地方挂起,并且可以重新在挂起处继续运行。
协程与线程与进程:
一个操作系统中可以有多个进程;一个进程可以有多个线程;同理,一个线程可以有多个协程。一个线程内的多个协程的运行是串行的,这点和多进程(多线程)在多核CPU上执行时是不同的。 多进程(多线程)在多核CPU上是可以并行的。当线程内的某一个协程运行时,其它协程必须挂起。
二 丶协程的创建与调用
协程是一个 IEnumerator 类型的特殊函数, 它通过 StartCoroutine 函数启动,协程是在 Mono 的底层调度中运行实现的, 所以执行协程的类必须继承 MonoBehaviour
using UnityEngine;
using System.Collections;
public class Main : MonoBehaviour
{
// 1.协程的创建声明
IEnumerator MyCoroutine(float delayTime)
{
// 执行第一步操作
Debug.Log("Start");
// 暂停协程(类似异步等待)
yield return new WaitForSeconds(delayTime);
// 执行第二步操作
Debug.Log("After delay");
}
public void Start()
{
// 2. 协程的调用
StartCoroutine(MyCoroutine(2.0f));
}
}
yield return 的返回值类型如下:
yield return null; // 下一帧再执行后续代码
yield return 6;//(任意数字) 下一帧再执行后续代码
yield break; //直接结束该协程的后续操作
yield return asyncOperation;//等异步操作结束后再执行后续代码
yield return StartCoroution(/*某个协程*/);//等待某个协程执行完毕后再执行后续代码
yield return WWW();//等待WWW操作完成后再执行后续代码
yield return new WaitForEndOfFrame();//等待帧结束,等待直到所有的摄像机和GUI被渲染完成后,在该帧显示在屏幕之前执行
yield return new WaitForSeconds(0.3f);//等待0.3秒,一段指定的时间延迟之后继续执行,在所有的Update函数完成调用的那一帧之后(这里的时间会受到Time.timeScale的影响);
yield return new WaitForSecondsRealtime(0.3f);//等待0.3秒,一段指定的时间延迟之后继续执行,在所有的Update函数完成调用的那一帧之后(这里的时间不受到Time.timeScale的影响);
yield return WaitForFixedUpdate();//等待下一次FixedUpdate开始时再执行后续代码
yield return new WaitUntil()//将协同执行直到 当输入的参数(或者委托)为true的时候....如:yield return new WaitUntil(() => frame >= 10);
yield return new WaitWhile()//将协同执行直到 当输入的参数(或者委托)为false的时候.... 如:yield return new WaitWhile(() => frame < 10);
三 丶协程的实现原理
IEnumerator 是一个迭代器类型, yield return 是协程中实现等待执行 的关键返回值,在unity的底层中编译器会自动根据 IEnumerator类型函数中的 yield return 语句把整个函数构建成一个可分段执行的迭代器状态机,每遇到一个 yield return 就会把它前面的代码分成一个代码段, 每个代码段都有一个yield return 返回值
迭代器可以将这些分好段的函数通过 MoveNext() 方法挨个挨个执行. 状态机会持有当前代码段的执行状态,执行完成后调用迭代器的 MoveNext() 方法 去执行下一段代码,直到 MoveNext() 为空表示协程执行完毕,
状态机中持有当前代码段的yield return的返回值 Current , 根据返回值 Current 的类型,执行不同的逻辑函数(这是类型都是unity已经封装好了的), 函数执行完成后调用 MoveNext(), 例如:类型为 WaitForSeconds(delayTime) 表示 执行一个传入时间为delayTime的计时器, 计时器结束则执行下一段函数
协程管理器管理所有通过StartCoroutine()方法启动的协程函数,它会在Mono的Update函数中,逐帧执行每个协程函数的状态机, 到这里我们就可以看出来协程是在主线程中逐帧执行的,而且代码是一段一段执行的.
四 丶代码参考
--------状态机类code,以及 yield return返回值 WaitForSeconds 类code-------
public class Coroutine
{
private IEnumerator _enumerator;
private object _currentYieldInstruction;
private bool _isCompleted;
public bool IsCompleted => _isCompleted;
public Coroutine(IEnumerator enumerator)
{
_enumerator = enumerator;
_currentYieldInstruction = null;
_isCompleted = false;
}
public void Update(float deltaTime)
{
if (_isCompleted) return;
// 处理当前的 yield 指令
if (_currentYieldInstruction is WaitForSeconds wait)
{
wait.remainingTime -= deltaTime;
if (wait.remainingTime <= 0f)
{
_currentYieldInstruction = null; // 指令完成,继续执行
}
}
// 如果没有等待指令或等待完成,推进迭代器
if (_currentYieldInstruction == null)
{
if (_enumerator.MoveNext())
{
_currentYieldInstruction = _enumerator.Current;
}
else
{
_isCompleted = true;
}
}
}
}
public class WaitForSeconds
{
public float remainingTime;
public WaitForSeconds(float time)
{
remainingTime = time;
}
}
--------------------------------管理器code:------------------------------------
public class CoroutineManager
{
private List<Coroutine> _activeCoroutines = new List<Coroutine>();
public Coroutine StartCoroutine(IEnumerator routine)
{
var coroutine = new Coroutine(routine);
_activeCoroutines.Add(coroutine);
return coroutine;
}
public void StopCoroutine(Coroutine coroutine)
{
_activeCoroutines.Remove(coroutine);
}
public void Update(float deltaTime)
{
foreach (var coroutine in _activeCoroutines.ToList())
{
coroutine.Update(deltaTime);
if (coroutine.IsCompleted)
{
_activeCoroutines.Remove(coroutine);
}
}
}
}
※特此声明: 内容可能会有一些个人的看法,与当前的认知水平有关,如有问题欢迎大家提出.
******************如有疑问或者有其他感兴趣的内容,欢迎在评论区留言*************************