zoukankan      html  css  js  c++  java
  • Unity 从各个点理解协程(知乎)

    什么是协同程序?什么是协程?

    Unity 协程是一个能够暂停协程执行,暂停后立即返回主函数

    执行主函数剩余的部分,直到中断指令完成后,从中断指令的下一行继续执行协程剩余的函数

    函数体全部执行完成,协程结束

    由于中断指令的出现,使得可以将一个函数分割到多个帧里去执行

    而在性能上,协程相比于一般函数并没有更多的开销

    协程的好处

    让原来要使用异步 + 回调方式写的非人类代码, 可以用看似同步的方式写出来

    能够分步做一个比较耗时的事情,如果需要大量的计算,将计算放到一个随时间进行的协程来处理,能分散计算压力

    协程的坏处

    协程本质是迭代器,且是基于 Unity 生命周期的,大量开启协程会引起 GC

    如果同时激活的协程较多,就可能会出现多个高开销的协程挤在同一帧执行导致的卡帧

    协程书写时的性能优化

    常见的问题是直接 new 一个中断指令,带来不必要的 GC 负担,可以复用一个全局的中断指令对象,优化掉开销

    在 Yielders.cs 这个文件里,已经集中地创建了上面这些类型的静态对象

    这个链接分析了一下

    协程是在什么地方执行?

    协程不是线程,不是异步执行

    协程和 Monobehaviour 的 Update 函数一样也是在主线程中执行

    Unity 在每一帧都会处理对象上的协程,也就是说,协程跟 Update 一样是 Unity 每帧都会去处理的函数

    经过测试,协程至少是每帧的 LateUpdate 后运行的

    参照 Unity 的生命周期图

    协程怎么结束?

    方法一:StopCoroutine(string methodName);

    方法二:StopAllCoroutines 暂停的是当前脚本下的所有协程

    方法三:gameObject.active = false 可以停止该对象上全部协程的执行,即使再次激活,也不能继续执行

        但注意 MonoBehaviour enabled = false 不能停止协程

        对比 Update 却是可以在 MonoBehaviour enabled = false 就中止

    原因:由于协程是在 StartCoroutine 时被注册到的 GameObject 上

       他的生命期受限于 GameObject 的生命期,因此受 GameObject 是否 Active 的影响

    结论:协程虽然是在 MonoBehvaviour 启动的(StartCoroutine)

       但是协程函数的地位完全是跟 MonoBehaviour 是一个层次的,不受 MonoBehaviour 的状态影响

    协程结束的标志是什么?

    如果最后一个 yield return 的 IEnumerator 已经迭代到最后一个时,MoveNext 就会返回 false

    这时,Unity 就会将这个 IEnumerator 从 cortoutines list 中移除

    只有当这个对象的 MoveNext() 返回 false 时,即该 IEnumertator 的 Current 已经迭代到最后一个元素了,才会执行 yield return 后面的语句

    中断函数类型

    null 在下一帧所有的 Update() 函数调用过之后执行

    WaitForSeconds() 等待指定秒数,在该帧(延迟过后的那一帧)所有 Update() 函数调用完后执行

    即等待给定时间周期, 受 Time.timeScale 影响,当 Time.timeScale = 0f 时,yield return new WaitForSecond(x) 将不会满足

    WaitForFixedUpdate 等待一个固定帧,即等待物理周期循环结束后执行

    WaitForEndOfFrame 等待帧结束,即等待渲染周期循环结束后执行

    StartCoroutine 等待一个新协程暂停

    WWW 等待一个加载完成,等待 www 的网络请求完成后,isDone=true 后执行

    协程的执行顺序

    开始协程 ->

    执行协程 ->

    遇到中断指令中断协程 ->

    返回上层函数继续执行上层函数的下一行代码 ->

    中断指令结束后,继续执行中断指令之后的代码 ->

    协程结束

    协程可以嵌套协程吗?

    可以,yield return StartCoroutine 就是,执行顺序是:

    子协程中断后,会返回父协程,父协程暂停,返回父协程的上级函数

    决定父协程结束的标志是子协程是否结束,当子协程结束后返回父协程执行其后的代码才算结束

    同一时刻同一脚本实例中能有多少个运行的协程?

    在一个 MonoBehaviour 提供的主线程里只能有一个处于运行状态的协程

    因为协程不是线程,不是并行的

    同一时刻、一个脚本实例中可以有多个暂停的协程,但只有一个运行着的协程

    协程和线程的区别?

    线程是利用多核达到真正的并行计算,缺点是会有大量的锁、切换、等待的问题

    而协程是非抢占式,需要用户自己释放使用权来切换到其他协程

    因此同一时间其实只有一个协程拥有运行权, 相当于单线程的能力

    协程是 C# 线程的替代品,是 Unity 不使用线程的解决方案

    使用协程不用考虑同步和锁的问题

    多个协程可以同时运行,它们会根据各自的启动顺序来更新

    其他注意点

    1、IEnumerator 类型的方法不能带 ref 或者 out 型的参数,但可以带被传递的引用

    2、在函数 Update 和 FixedUpdate 中不能使用 yield 语句,否则会报错, 但是可以启动协程

    3、在一个协程中,StartCoroutine()和 yield return StartCoroutine()是不一样的

       前者仅仅是开始一个新的 Coroutine,这个新的 Coroutine 和现有 Coroutine 并行执行

       后者是返回一个新的 Coroutine,是一个中断指令,当这个新的 Coroutine 执行完毕后,才继承执行现有 Coroutine

    原文:https://zhuanlan.zhihu.com/p/59619632

    *** |  以上内容仅为学习参考、学习笔记使用  | ***

  • 相关阅读:
    iOS开发-文件管理(一)
    浅析栈区和堆区内存分配的区别
    浅谈Block传值-匿名函数(代码块)
    cell的各种使用和赋值 总结
    类方法和对象方法的区别
    属性传值 ,代理传值,单例
    类目,延展,协议
    任意点 并查集
    Codeforces 145E. Lucky Queries 线段树
    Codeforces 103B. Cthulhu 并查集运用
  • 原文地址:https://www.cnblogs.com/ChenZiRong1999/p/13389243.html
Copyright © 2011-2022 走看看