Coroutine 协程

协程的关键字是 IEnumerator
这是迭代器的意思,那么什么是迭代器?

先请看下面的代码:
定义一个Test类,实现IEnumerable接口:

    class Test : IEnumerable
    {
        public IEnumerator GetEnumerator()
        {
            for (int i = 0; i < 5; i++)
            {
                yield return "test" + i;
            }
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

说一下 IEnumerableIEnumerator 的关系:

  1. IEnumerable是一个接口,代表其是可枚举的。
  2. IEnumerator对象就是一个迭代器(iterator:MoveNext(),Reset(),Current)。

用上面的类,写一个测试程序:

            Test test = new Test();
            foreach (string s in test)
            {
                Console.WriteLine(s);
            }

此时输出结果为:


输出结果

可以猜到,输出的就是 yield return xxx; 后面的xxx。
那么换一种写法试试:

            Test test = new Test();
            var e = test.GetEnumerator();
            while (e.MoveNext())
            {
                Console.WriteLine(e.Current);
            }

这种写法就是基于迭代器(iterator)的方式。先来看一下迭代器的定义:

    public interface IEnumerator
    {
        bool MoveNext();
        void Reset();
        Object Current {  get;  }
    }

从定义可以理解,使用foreach这样的枚举操作的时候,枚举数被定为在集合的第一个元素前面。
使用迭代器在执行迭代,首先会执行 MoveNext(), 如果返回true,说明下一个位置有对象,然后此时将Current设置为下一个对象,这时候的Current就指向了下一个对象;如果返回false,说明下一个位置没有对象,此时结束遍历。

虽然看起来和遍历数组一样,但本质上是把其包装成了一个迭代器执行,每次执行到yield的时候,程序就把需要的东西传递出去,再停住,不会继续往下执行。
由于这里yield下面没有什么其他语句,所以就会依旧 i++ ,继续执行下去,然后又返回一个值。

那么回到Unity3d中,协程的用法如下:

        void Start()
        {
            StartCoroutine(DoSomething());
        }

        IEnumerator DoSomething()
        {
            DoBefore();
            yield return new WaitForSeconds(1f);
            DoAfter();
        }

这里Unity3d的运行效果,在 Start() 方法开启协程后,程序会执行 DoBefore() 函数,直到遇见第一个yield,就会暂时把协程挂起,直到下一帧的Update渲染完成之后再获取到迭代器当前的 Current 对象,此时就是 WaitForSeconds 这个对象。
通过此对象,判断是否还需要进行等待,再进行 DoAfter() 的回调。

我们可以写伪代码来表述内部的逻辑:

void StartCoroutine(IEnumerator e)
{    
    if (!e.MoveNext())
    {        
        return;
    }    

    var obj = routine.Current;    
    if (obj is WaitForSeconds)
    {        
        //判断是否到达时间 如果超过了设置时间 就继续回调
    }
}

在注释处,会利用现有的定时器机制,注册定时器,并继续进行 DoAfter() 的回调。

你可能感兴趣的:(Coroutine 协程)