协程

最后更新于 2022-12-11 354 次阅读


概念

协程 (Coroutine) 是大部分现代编程环境都提供的一个非常有用的机制。它允许我们把不同时刻发生的行为,在代码中以线性的方式聚合起来。与基于事件与回调的系统相比,以协程方式组织的业务逻辑,可读性相对好一些。

Unity 内的协程实现是传统协程的简化——在主线程内每一帧给定的时间点上,引擎通过一定的调度机制来唤醒和执行满足条件的协程,以实际上的分时串行化执行回避了协程之间的通信问题。

  • 函数体全部执行完成,协程结束。
  • 由于中断指令的出现,使得可以将一个函数分割到多个帧里去执行。
  • 协程不是进程,也不是线程,它就是一个特殊的函数——可以在某个地方挂起,并且可以重新在挂起处继续运行。
  • Unity的协程会在每帧结束之后去检测yield的条件是否满足,满足则执行yield return之后的代码。

协程与普通方法的区别

  • 普通方法:被调用时,原来执行的部分保留现场,停止执行,然后去执行要调用的方法,并且,被调用的方法执行完之后才能返回到调用前的状态接着往下执行。
  • 协程:执行不用等协同方法执行完再执行调用之前原来方法的代码,而是两者异步执行。

相关问题

协程是不是多线程

协程不是多线程,它与主线程同时运行,它在主线程运行的同时开启另一段逻辑处理。
类似一个子线程单独出来处理一些问题,性能开销较小

可以同时运行多个协程吗

在一个MonoBehaviour提供的主线程里只能有一个处于运行状态的协程,而其他协程处于休眠状态。
协程实际上是在一个线程中,只不过每个协程对CPU进行分时,协程可以访问和使用unity的所有方法和component。

协程的好处和坏处

好处

  • 让原来要使用异步 + 回调方式写的非人类代码, 可以用看似同步的方式写出来。
  • 能够分步做一个比较耗时的事情,如果需要大量的计算,将计算放到一个随时间进行的协程来处理,能分散计算压力

坏处

  • 协程本质是迭代器,且是基于unity生命周期的,大量开启协程会引起gc
  • 如果同时激活的协程较多,就可能会出现多个高开销的协程挤在同一帧执行导致的卡帧

性能优化

https://blog.csdn.net/liujunjie612/article/details/70623943

协程是在什么地方执行?

协程不是线程,不是异步执行;协程和monobehaviour的update函数一样也是在主线程中执行
unity在每一帧都会处理对象上的协程,也就是说,协程跟update一样都是unity每帧会去处理的函数
经过测试,协程至少是每帧的lateUpdate后运行的。

实现

协程的实现需要在Unity中继承MonoBehaviour并使用C#的迭代器IEnumrator,格式如下所示:

IEnumrator 函数名(形参表) //最多只能有一个形参
{
    yield return xxx; //恢复执行条件
    //方法体
}

在IEnumerator类型的方法中写入需要执行的操作,遇到yield后会暂时挂起,等到yield return后的条件满足才继续执行yield语句后面的内容。

开启与实现

相关测试

协程的开启

开启协程需要使用StartCoroutine()方法:

  • 开启无参数的协程:
    StartCoroutine(协程名());或StartCoroutine("协程名");
  • 开启单参数的协程:
    StartCoroutine(协程名(参数));或StartCoroutine("协程名",参数);
  • 开启多参数的协程:
    StartCoroutine(协程名(参数1,……));
void StartCoroutine()//开启协程的函数
{
    IEnumerator coroutine = Test(5, 6);
    StartCoroutine(coroutine);
}
public IEnumerator Test(int a, int b)//协程
{
    //等待帧画面渲染结束
    yield return new WaitForEndOfFrame();
    a=2;
    b=3;
}

协程的结束

结束协程有两种情况:

  • 当协程的方法体执行完毕将会自动结束
  • 调用StopCoroutine();方法中止协程执行

中止协程的几种情况:

  • 中止所有协程:
    StopAllCoroutines();
  • 使用对象实例中止指定协程
Coroutine c;
void Start()
{
    c = StartCoroutine(CountSeconds());
}
    void Update()
{
    if (Input.GetKeyDown(KeyCode.J))
    {
        StopCoroutine(c);
    }
}

使用字符串中止指定协程:

StartCoroutine("协程名");
StopCoroutine("协程名");

Tips

  • 只有以协程名字符串启动的协程可以用此方法中止 既:StartCoroutine(“协程名”);或StartCoroutine(“协程名”,参数);
  • 允许使用StopCoroutine(“协程名”);中止协程 不允许使用直接调用协程方法的方式中止指定协程 既:StopCoroutine(协程名([参数]));不被允许

yield 协程回复条件语句