zoukankan      html  css  js  c++  java
  • 深入浅出!从语义角度分析隐藏在Unity协程背后的原理

    Unity的协程使用起来比较方便,但是由于其封装和隐藏了太多细节,使其看起来比较神秘。比如协程是否是真正的异步执行?协程与线程到底是什么关系?本文将从语义角度来分析隐藏在协程背后的原理,并使用C++来实现一个简单的协程,以揭开协程的神秘面纱。(文内代码为截图,可点击放大查看。)

    一、什么是协程

    简单来说,协程是一个有多个返回点的函数。一般来说一个函数只有一个返回点,函数的调用者调用一次后,函数的生命周期就结束了。而对于协程来说,其生命周期由调用者来决定,可以通过返回值来决定如何进行一次调用以及如何结束调用。

    由于协程返回的是一系列的值,每一个yield return对应一次返回。用迭代器作为返回类型是比较好的选择,可以简单的认为每一个yield return对于了迭代器中的一个元素。

    Unity的C#代码中一个协程的返回值通常是IEnumerator类型,IEnumerator 接口有两个方法,分别是 Current 和 MoveNext。我们可以简单的认为:协程就是一个返回迭代器的函数,一开始迭代器的Current指向函数的开头,每执行一次MoveNext,Current就指向下一个yield return 返回的值。例如:

    二、协程的核心,神奇的yield

    我们来看看上而代码示例中的函数coroutine:

    上面代码神奇的地方在于关键词yield,为什么可以调用多次yield return返回一组值,并且该函数的返回值变成了Enumerator类型?这其实就是隐藏在协程背后的核心原理。有很多关于协程的核心原理的分析及实现的文章,大多都是从系统底层的角度来分析,使用了汇编,goto语句或者是C 语言的 setjmp 和 longjmp来实现了协程,虽然分析得比较透彻,但对读者所掌握的知识要求比较高,其中的一些概念比较晦涩,难于理解。

    实际上从语义角度来看,协程的工作原理比较简单,任何支持闭包的语言都可以实现协程。下面我们从语义的角度来解释协程:

    1.每一个yield return被包装成一个函数(简称为Y函数),如果该函数使用了外层的局部变量,将形成一个闭包

    2.Y函数的返回值为yield传入的对象和下一个yield return所包装成的函数

    3.对迭代器的MoveNext调用等价于对Y函数的调用

    4.最后一个yield return包装成的Y函数返回yield传入的对象和一个空函数以代表迭代结束(即调用MoveNext时返回false)

    三、简单协程的C++实现

    以下是根据协程在语义上的解释,用C++的实现一个简单协程示例:

    一共不到60行代码,就实现了简单的协程。对于上示例中的coroutine函数,除了语法上有一些差异,语义已经和C#的协程完全一协了,甚至我们可以引入一个宏让其长得更像C#的协程,例如:

    虽然以上代码段看起来已经比较像Unity的协程了,但是遗憾的是代码还不够简洁,有一点点语法噪音(函数的结尾部分多了一些没意义的括号)语法也不够统一(最后一行是return yield)。

    四、For循环

    第三节我们讲了协程的简单的实现,但是如果我们如果在for循环中使用yield还存在问题,比如以下代码段:

    coroutine1函数返回的迭代器中只包含一个值1,而不是我们期望的1~10。因此对于协程中的循环我们需要特殊处理。分析以上代码,我们期望的是对于循环中的每一个i,调用yield生成一个coroutine node,然后将这些node合并成一个链表返回。因此我们可以将协程中的for语句翻译成一个For函数,代码如下:

    Combine函数实现将两个Coroutine Node连接在一起合并成一个链表返回,其定义代码如下:

    有了For函数,可以将coroutine1实现可以改成如下:

    虽然在语法上该函数有一些丑,但是是从语义上完全是等价以下C#代码的:

    以下是一个更加复杂的示例:

    语义上等价的C#代码为:

    五、总结

    本文从语义的角度来解释了协程的运行机制,并使用C++实现了简单的协程,但仍有部分内容未讨论到,比如怎样在For循环中实现break,yield break等,其实要实现这些并不困难,感兴趣的同学可以自行去研究。

    回到引言中提到的两个问题:

    协程是否为异步执行?严格意义上来讲,协程并不是异步执行的,但是调用者可以分时间片去执行每一个yield,让程序看起来像是异步的。协程和线程是什么关系?协程与线程之间没有严格的对应关系,但是可以结合使用,隐藏一些不必要的细节以简化编程,比如unity的WWW。

    来源:公众号<Gad-腾讯游戏开发者平台>

    作者:Tseclam

    原题:程序|深入浅出!从语义角度分析隐藏在Unity协程背后的原理

    产品经理必读文章

    一份鹅厂产品经理自我成长的文档

    腾讯内部培训手册:产品经理晋升路上的那些坎儿

    重磅干货|QQ运营女王:小白到大神,要跨过哪些坑

    不声不响我就当了产品经理......

    腾讯产品法则:从需求分析到需求管理,做产品需求最全的方法都在这了

    为何有些产品经理画的线框图总被吐槽

    什么是「好产品」?

    陈会华:写给喜欢数据分析的初学者

    体验至上的时代,交互设计师能为游戏做点什么?

    熊一冬:做网游不是开宝箱-产品运营大循环思

  • 相关阅读:
    HDU 2433 Travel (最短路,BFS,变形)
    HDU 2544 最短路 (最短路,spfa)
    HDU 2063 过山车 (最大匹配,匈牙利算法)
    HDU 1150 Machine Schedule (最小覆盖,匈牙利算法)
    290 Word Pattern 单词模式
    289 Game of Life 生命的游戏
    287 Find the Duplicate Number 寻找重复数
    283 Move Zeroes 移动零
    282 Expression Add Operators 给表达式添加运算符
    279 Perfect Squares 完美平方数
  • 原文地址:https://www.cnblogs.com/lancidie/p/8637558.html
Copyright © 2011-2022 走看看