协程原理 Unity/C#

 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);
            }
        }
    }
}

※特此声明: 内容可能会有一些个人的看法,与当前的认知水平有关,如有问题欢迎大家提出.
******************如有疑问或者有其他感兴趣的内容,欢迎在评论区留言*************************

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值