原文
对上述链接的解析,感觉这篇文章写得挺好的,特此记录以供分析。
Activation Frame
这玩意儿我也没想出来什么翻译好一点,总觉得激活帧差点意思。可以把该帧当作一个内存块,专门用来存储suspend point(暂停点,该概念将会在下文进一步讲解)和返回地址(协程的返回值的地址)。相当于是当程序暂停去执行其他操作的时候,激活帧用来存储被执行的操作回来的地址。
所以其实激活帧可以理解为,提供激活信息的帧(包括执行的参数,返回的地址等),主要是为暂停操作服务。
该帧的分配位置主要取决于协程操作的生命周期,由于协程可以在不销毁激活帧的时候进行暂停,也就无法确保暂停时是否有必要将所有的资源全都保存在内存里,毕竟对于计算机来说内存资源就是生命,所以将之分为了两块以满足不同的需求:
- coroutine frame: 挂起时持续存在
- stack frame: 挂起时资源将被释放
Call Operation
调用操作,主要是为暂停操作进行一些准备工作,也可以理解为,你家孩子要出门了,你要为他打包点行李(哈哈哈哈哈也可以这么理解吧),首先要将被调用函数所需要的参数写到一个新的激活帧里以供使用,其次,在执行被调用函数前,还需要调用者把恢复操作的地址写到新的激活帧里。
Return Operation
返回操作,主要执行三个步骤:
- 先将返回值存储
- 释放本地变量(保存在堆上)
- 在返回前允许additional logic(可以实现自定义)
协程操作
协程操作主要有三点,suspend, resume, destroy, 接下来将一个一个进行分析。
suspend
暂停操作,也就是所谓的挂起操作,会在suspend_point的位置进行暂停,该暂停点主要通过co_await
或co_yield
来进行标注。在暂停操作前,需要先做到以下两点:
- 确保寄存器中的值都被写入了coroutine frame中(在挂起时不会被释放)
- 在coroutine frame中记录暂停点(可能有好几个暂停点)。还需要为恢复操作和销毁操作提供信息,恢复的地址和所需要销毁的资源。
resume
恢复操作,根据上文所述的coroutine frame中保存的suspend point来决定返回的位置。在调用时将会分配一个新的stack frame用于暂时性地存储资源,并将在返回时释放。
destroy
销毁操作与恢复操作类似:
- 都需要重新激活激活帧
- 都会分配一个stack_frame
区别在于销毁操作并不会回到suspend point,而会在释放内存前调用范围内地所有本地变量的析构函数,因为这些资源都被保存在堆上,需要手动释放。
说得简单点就是,你家孩子出门上大学了,这个出门的操作就是暂停操作,你需要在你家的激活帧里存储一些信息,比如说孩子上大学的时间,孩子想要回家的地址。当孩子不在家的时候(也就是当前函数被暂停后),家里的有些东西就可以扔掉了,比如说家里给孩子订的牛奶,就可以停掉了,这就是stack frame,而有些东西是不能扔掉的,比如说孩子的床,不然等孩子回来了(恢复操作),他就没地方住了,这个资源就是coroutine frame。
恢复操作就是孩子还是回到了自己的家,但是他在上大学期间,住在学校宿舍的床上(stack frame),回家之后仍然会释放床位,而销毁操作就是孩子大学毕业之后找到了工作,不回家了,但是仍然需要把学校宿舍的床释放出来(释放资源)。
协程主要是关于资源的释放和使用,以及资源的所有权的问题,在使用过程中需要注意这一点。