Unity协程的原理和协程与线程与进程的区别

这篇文章适合有一定协程使用基础的开发者看,不了解协程是什么概念的可以先看看官网

Unity协程的原理

说起协程的原理,就要谈起c#的迭代器功能:
简单来说,类继承了IEnumerable接口并且实现了GetEnumerator()方法就可以使用foreach去遍历类,遍历输出的结果是根据GetEnumerator()的返回值IEnumerator确定的。这种写法有些繁琐,c#就推出了yield关键字来对其进行简化。

简化成这样了,代码如下所示

IEnumerator TestCoroutine()
{
	Debug.Log("1111");
    yield return null;              //返回内容为null
	Debug.Log("2222");
    yield return 1;                 //返回内容为1
	Debug.Log("3333");
    yield return "sss";             //返回内容为"sss"
	Debug.Log("4444");
    yield break;                    //跳出,类似普通函数中的return语句

    yield return 999;               //由于break语句,该内容无法返回
}

void Start()
{
    IEnumerator e = TestCoroutine();
    while (e.MoveNext())
    {
        Debug.Log(e.Current);       //依次输出枚举接口返回的值
    }
}

上面的代码基本上用到的就是纯粹的c#的迭代器功能,

IEnumerator e = TestCoroutine();

执行的时候其实方法并没有执行,只是单纯获得一个IEnumerator类型的返回值e
在接下来第一次调用

e.MoveNext()

这个方法的时候,TestCoroutine就会执行完yield return null; 然后会停止,e.MoveNext()返回true,e.Current的值是null
第二次调用的时候,TestCoroutine就会执行完yield return 1; 然后会停止,e.MoveNext()返回true,e.Current的值是1
第三次调用的时候,TestCoroutine就会执行完yield return "sss"; 然后会停止,e.MoveNext()返回true,e.Current的值是"sss"
第四次调用的时候,TestCoroutine就会执行完yield break; 然后会停止,e.MoveNext()返回false,e.Current的值是"sss",因为返回的是false,所以while循环会退出。
这里对其结果进行解释说明下,MoveNext执行的时候,方法就会挨个执行yield,执行一次然后停止,当yield 后面跟了return的时候,MoveNext的返回值就是true ,即使return的是null,如果yield 后面 没有跟 return,则MoveNext就会返回false,如果这时再继续执行MoveNext,则会报错。
这是c#的迭代器的运行过程,关于c#如何实现迭代器的可以参考微软的Iterators (C#)

Unity的协程是基于此实现的,在Unity中,协程的等待主要有三种,

  • 等待固定时机如等待一次绘制帧,等待一次物理帧
  • 等待开发者定义的实际时间,如等待两秒
  • 等待另外一个协程方法执行结束

开发者自己实现Unity的协程方案如下:
对于第一种,实现原理是在每次LateUpdate调用的时候执行一次MoveNext()方法,只要返回值不为false,下一帧就可以继续执行。因为Unity中协程的调用顺序是在Update之后,LateUpdate之前,所以决定放在LateUpdate中,参考Unity的生命周期
对于第二种,实现原理是在每次Update或者FixedUpdate调用的时候通过+= Time.deltaTime或者 += Time.FixedDeltaTIme进行一次时间叠加计算,当倒计时达到的时候就执行一次MoveNext方法,然后如果MoveNext返回true就重置倒计时
对于第三中,实现原理是先启动需要等待的协程,在每次其执行MoveNext的时候判断一次MoveNext的返回值,返回值为false的时候,就启动正在等待的协程。
参考博客:Unity 协程原理探究与实现

协程与线程与进程的区别

在这里插入图片描述
协程和线程和进程的区别可以用上图来表示。
上图中,进程里面可以有两个或者多个线程,Unity工作最主要的是主线程,其余的线程主要是用来渲染,而一般在主线程里面开辟的多个协程都是在主线程里面的。
一个应用程序一般对应一个进程,一个进程一般有一个主线程,还有若干个辅助线程,线程之间是平行运行的,在线程里面可以开启协程,让程序在特定的时间内运行。
图中一个进程里面两个线程真的会同时运行,就好像两个人同时在工作一样,而在Unity中,一个线程里面开启多个协程,假设每个协程都是一帧执行一次的,是简单的把一帧的时间因为多个协程的原因延长了,协程并不会真正的并行运行,只是延长的时间很短,这样每一帧就执行了多个协程,看起来就好像这些协程并行运行了一样。

进程拥有自己独立的堆和栈,既不共享堆,亦不共享栈,进程由操作系统调度。
线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程亦由操作系统调度(这里指标准线程)。
协程和线程一样共享堆,不共享栈,协程由开发者在协程的代码里显示调度。

协程和线程的区别是:协程避免了无意义的调度,死锁等问题隐患,由此可以提高性能和安全性,使用起来也比线程更加方便。但程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力。

还有个区别是,多开的线程不支持直接对Unity组件或者Monobehaviour继承类访问。最近听朋友说他试了除了UGUI这部分的组件其他都可以了,也没尝试,不太清楚。 协程因为本质还是主线程,是能直接访问各种组件的。

参考链接: unity 进程和协程原理与线程的区别(转)

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

染指流年丨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值