協同線程

最早接觸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;
    }
}


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