协同线程

最早接触coroutine是在02年使用unreal engine时。unreal script通过支持latent function和state代码实现了coroutine的思想。由于unreal script属于私有语言,整个语言设计的针对性比较强,所以虽然很好用,但是在语言的通用性上的没有任何的考虑。03年开始接触lua时,被他的coroutine设计吸引住了。把lua的coroutine用在游戏中可谓再合适不过了,而且使用起来更自由一些。Unity中也支持coroutine,虽然思想上和用法上和unreal engine以及lua的大同小异,实现层面还是有本质的区别的。理解Unity中的coroutine的运作机制对于正确的使用coroutine是有很大帮助的。

Unity coroutine的实现机制

先来说说lua coroutine。在lua中,coroutine是通过一个独立的lua state来实现,这个state中保存了coroutine的运行堆栈。在corotine程序中,可以随时暂停执行,保存调用堆栈,也可以在任意时刻恢复执行。lua的coroutine可以说是真正的coroutine。

与lua不同,Unity所使用的mono,也就是.net虚拟机本身并不支持coroutine,coroutine功能是在语言层通过Iterator Blocks模拟出来的。C#将Iterator Block代码转化为一个IEnumerator class代码,并返回这个IEnumerator,外部可以通过调用这个IEnumerator对象的MoveNext()函数来运行一次coroutine。

模拟Unity coroutine

为了理解unity coroutine,自己用C#实现了一个Coroutine系统。虽然没有Unity的source,自己感觉应该和Unity内部实现方法近似的。
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public interface IYYieldInstruction
{
    bool isFinished();
}

public class YCCoroutine : IYYieldInstruction
{
    private YCContext c_;
    public YCCoroutine(YCContext c)
    {
        c_ = c;
    }
    public bool isFinished()
    {
        return c_.isFinished();
    }
}
public class YCWait : IYYieldInstruction
{
    private float seconds_;
    public YCWait(float sec)
    {
        seconds_ = sec;
    }
    public bool isFinished()
    {
        seconds_ -= Time.deltaTime;
        return (seconds_ < 0);
    }
}

public class YCContext
{
    IEnumerator coroutine_;
    IYYieldInstruction curYield_;
    bool isFinished_;
    public YCContext(IEnumerator c)
    {
        coroutine_ = c;
        curYield_ = coroutine_.Current as IYYieldInstruction;
    }
    public void step()
    {
        if(isFinished_)
            return;
        if(curYield_ != null && curYield_.isFinished())
            curYield_ = null;
        if(curYield_ == null)
        {
            if(!coroutine_.MoveNext())
                isFinished_ = true;
            else
                curYield_ = coroutine_.Current as IYYieldInstruction;
        }
    }
    public bool isFinished() { return isFinished_; }
}

public class YCoroutineManager : MonoBehaviour
{
    private List<YCContext> contexts_ = new List<YCContext>();

    public YCCoroutine startCoroutine(IEnumerator c)
    {
        YCContext context = new YCContext(c);
        YCCoroutine cr = new YCCoroutine(context);
        contexts_.Add(context);
        return cr;
    }

    void Update()
    {
        for (int i = 0; i < contexts_.Count; i++)
        {
            YCContext c = contexts_[i];
            c.step();
            if (c.isFinished())
            {
                contexts_.Remove(c);
                i--;
            }
        }
    }

    IEnumerator Start()
    {
        Debug.Log(Time.frameCount);
        yield return startCoroutine(c1());
        Debug.Log(Time.frameCount);
    }
    IEnumerator c1()
    {
        Debug.Log(Time.frameCount);
        yield return startCoroutine(c2());
        Debug.Log(Time.frameCount);
    }
    IEnumerator c2()
    {
        Debug.Log(Time.frameCount);
        yield break;
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章