欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-07-10 15:50:27
这篇文章适合有一定协程使用基础的开发者看,不了解协程的可以看看官网说起协程的原理,就要谈起c#的迭代器功能简单来说,类继承了IEnumerable接口并且实现了GetEnumerator()方法就可以使用foreach去遍历类,遍历输出的结果是根据GetEnumerator()的返回值IEnumerator确定的。这种写法有些繁琐,c#就推出了yield关键字来对其进行简化。简化成这样了,代码如下所示IEnumerator TestCoroutine(){ yield return nul...

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

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的协程方案如下:
对于第一种,实现原理是在每次Update或者FixedUpdate调用的时候执行一次MoveNext()方法,只要返回值不为false,下一帧就可以继续执行
对于第二种,实现原理是在每次Update或者FixedUpdate调用的时候通过+= Time.deltaTime或者 += Time.FixedDeltaTIme进行一次时间叠加计算,当倒计时达到的时候就执行一次MoveNext方法,然后如果MoveNext返回true就重置倒计时
对于第三中,实现原理是先启动需要等待的协程,在每次其执行MoveNext的时候判断一次MoveNext的返回值,返回值为false的时候,就启动正在等待的协程。
参考博客:Unity 协程原理探究与实现

协程与线程与进程的区别

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

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

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

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

本文地址:https://blog.csdn.net/weixin_43149049/article/details/107304573