未挂起协同例程由机器调用栈机制来处理,其中该栈随着作出递归调用和从递归调用返回而增长和收缩。然而,在协同例程挂起时,执行附加的调用栈处理。发出挂起消息,并且调用栈的整个能恢复部分被移除并被复制到堆。将控制返回给驱动程序方法(恢复器)的帧被复制到调用栈,从而协同例程的恢复不会递归地重新激活整个调用栈。相反,恢复器仅重新激活被称为叶帧的最顶部或最当前帧。在协同例程挂起时,其不返回到其调用者,而是返回到已重新激活它的恢复器。
【技术实现步骤摘要】
在线性栈上高效恢复协同例程
技术介绍
在计算机科学中,协同例程是通常概括子例程并且可允许多个入口点以进行挂起 和恢复执行的程序组件。协同例程有时被用于实现协作任务、迭代器、无限列表和管道。虽 然子例程通常是后进先出构造,但协同例程的寿命可按其用途来规定。虽然子例程只能返 回一次,但协同例程可返回或让步一次以上。虽然子例程的开始通常是其唯一入口点,但协 同例程的开始是第一入口点且后续入口点可跟随让步(yield)命令。例如,同子例程一样, 在协同例程中让步可将结果和控制返回给调用方协同例程,但不同于子例程,在该协同例 程下次被调用时,执行可刚好在让步调用之后开始而非在协同例程开头开始。由于协同例程可返回多次,因此有可能在后续调用时返回附加的值。其中后续调 用产生附加结果的协同例程常常被称为生成器。传统子例程使用可在程序执行开头被预分 配的单个栈。相反,由于协同例程能够作为对等体调用其他协同例程,因此通常为协同例程 分配附加的栈。有时,预分配栈或者高速缓存预先分配的栈。生成器还一般化子例程,并且常常被用于简化迭代器的编写。生成器中的让步语 句通常不指定要跳转到的协同例程,而是将值回传给父例程。然而,仍然有可能在顶级分派 器例程的辅助下在生成器设施上实现协同例程,顶级分派器例程将控制显式地传递给从生 成器传回的令牌所标识的子生成器。许多流行编程语言(包括C、C++和C#)由于基于栈的子例程实现的限制因而在该 语言内不直接支持协同例程。在其中协同例程将在逻辑上被用于在可能的情况下实现一机 制的情形中,通常创建使用布尔标志和其他状态变量的组合来在调用之间维护内部状态的 子例程。条件语句导致基于状态变量的值而执行不同代码路径。或者,实现开关语句形式 的显式状态机。提供使用协同例程的替换方案的方法是使用线程。线程提供用于管理基本上同时 执行的代码片断的实时协作交互的能力。线程通常被优先调度。协同例程通常不是。由于 线程可在任何时刻被重新调度且能并发地执行,因此使用线程的程序必须注意锁定。相反, 由于协同例程只能在程序中的特定点被重新调度并且不并发地执行,因此使用协同例程的 程序常常完全避免了锁定问题。在高级语言中实现协同例程的一种方法放弃了便携性。相反,处理器系列专用的 实现对于函数是用汇编语言编写的,以节约和恢复协同例程上下文。必须注意这些函数的 使用和编写,从而在协同例程共享相同栈时位于该栈上的变量不被覆写。因此,通常对于高 级语言中基于栈的协同例程,需要函数来创建替换栈并在替换栈之间跳转。可提供第三机 器专用函数以创建用于新协同例程的上下文。传统上,协同例程的栈大小是固定的且在执 行期间不能增长。通常,程序分配比所需的更大的栈以避免潜在的栈溢出。概述在使用单个线性机器调用栈在没有直接协同例程支持的语言中执行的计算机系 统中,驱动程序例程控制协同例程执行。当协同例程最初被另一协同例程调用时,被调用的 协同例程被放到栈上调用者的帧之上。协同例程被首次挂起时,其所有栈帧向下直至驱动程序栈帧并且包括驱动程序栈帧被复制到堆。然而,若协同例程被再次挂起,则其栈帧不被 再次复制到堆。当协同例程恢复时,驱动程序例程仅将叶例程放到栈上。尽管该方法中的 单个挂起对于深度d的栈将花费0(d),但由于每一帧仅被复制到堆一次,因此该方法的总 分摊成本为0 (m),其中m是曾挂起的帧数,且m通常小于被调用的总帧数η。若在叶例程中抛出异常,则驱动程序例程检测或抓住该异常。驱动程序在栈上用 与调用者相关联的栈帧来代替与被调用者(被调用)叶例程相关联的栈帧。驱动程序执行 调用方例程,并且异常被再次抛出。提供本概述以便以简化形式介绍将在以下详细描述中进一步描述的一些概念。本 概述并非旨在标识出要求保护的主题的关键特征或必要特征,亦非旨在用于限定要求保护 的主题的范围。附图简述在附图中附图说明图1示出根据本文所公开主题的各方面用于在线性栈上高效恢复协同例程的系 统100的示例;图2是根据如关于图1所描述的本文所公开主题的各方面用于在线性栈上高效恢 复协同例程的方法200的示例的流程图。图3是示出其中可实现本文所公开主题的各方面的计算环境的示例的框图;以及图4是根据本文所公开主题的各方面的集成开发环境的示例的框图。详细描述概览根据本文所公开主题的各方面,未挂起协同例程由调用栈机制来处理,其中该栈 随着作出调用和从调用返回而增长和收缩。然而,在协同例程首次挂起时,执行附加的调用 栈处理。发出挂起消息,并且调用栈的整个能恢复部分被从机器调用栈移除并被复制到堆。 将控制返回给驱动程序方法(恢复器)的帧被复制到调用栈,从而恢复不会递归地重新激 活整个调用栈。相反,恢复器仅重新激活被称为叶帧的最顶部或最当前帧。在协同例程挂 起时,其不返回到其调用者,而是返回到已重新激活它的恢复器,该恢复器检索将控制返回 到的合适地址。由编译器执行的状态机转换不循环以重复地重新激活被调用者直至被调用栈返 回。从调用方状态机的观点而言,当被调用者返回时,其要么同步地返回,在这种情形中,结 果(或异常)被自然地消耗且处理继续进行;要么返回包括挂起,在这种情形中,处理被挂 起,且期望处理将不恢复直至被调用者完成并且恢复器返回其结果。在线性栈上高效恢复协同例程术语协同例程是用于动作序列的通用术语。协同例程类似于程序,除了与程序不 同的是,协同例程可在执行期间被暂停(挂起)并在以后恢复。恢复可通过客户机请求来 自协同例程的输出来发起。例如,在一些编程语言中,迭代器可被实现为协同例程,其任务 是按需产生集合的元素。一旦该协同例程已产生集合的一元素,其暂停。在需要下一个元 素时,该协同例程恢复。该过程继续,直至不需要更多元素或者直至已产生了集合中的所有 元素。恢复可通过输入变得可用来驱动或发起。例如,协同例程可在所请求数据不可用时暂停,这可能在较长等待时间通道上请求数据(例如,从盘读取或者从网络访问信息等) 时发生。恢复可由回调或事件发起或者可在所请求数据变得可用时以其他方式触发。这种 类型的情景很可能在采用异步编程技术时遇到。通常,当一个程序调用另一个程序时,调用方程序等待结果并随后调用下一个程 序,等待其结果,依此类推。相反,可从另一协同例程递归地调用一个或多个协同例程达到 无论任何递归深度。在第一协同例程在递归协同例程调用链中调用第二协同例程时,当第 二协同例程暂停时,第一协同例程暂停,依此类推。递归程序调用和子例程调用在传统上是经由称为机器调用栈(也称为执行栈、控 制栈、函数栈或运行时栈)的低级数据结构来实现的。调用栈保持跟踪每个有效被调用者 在完成执行时将控制返回到的点。活跃被调用者是已被调用但尚未通过返回调用者来完成 执行的被调用者。被调用者使调用者的返回地址进栈,并且在被调用者完成时,被调用者使 该返回地址出栈并将控制转移到该出栈的地址。因此,被放到栈上的信息随着程序执行而 增长和收缩。所描述的调用栈自动支持串行世界中的递归。取决于语言、操作系统和机器环 境,调用栈可服务附加函数,包括存储本地数据、参数、逻辑运算、指向当前实例的指针(该 指针(this pointer))、外围例程上下文和返回状态。机器调用栈数据结构由栈帧构成,栈帧是包含被调用者状态信息的取决于机本文档来自技高网...
【技术保护点】
1.一种系统(100),包括:处理器(142)和存储器(144),所述存储器包括配置成使所述处理器(142)执行以下动作的模块(106):在利用固有地不能挂起和恢复协同例程的单个线性机器调用栈的计算机环境中挂起和恢复包括调用方协同例程(110)和被调用协同例程(112)的协同例程,其中仅响应于检测到所述被调用协同例程的挂起,所述模块:保存先前未保存的所述单个线性机器调用栈的能恢复部分,所述单个线性机器调用栈的所述能恢复部分包括所述调用方协同例程的栈帧、所述被调用协同例程的栈帧以及恢复器的栈帧;将所述单个线性机器调用栈的所述能恢复部分从所述单个线性机器调用栈移除;将恢复器的返回地址放到所述单个线性机器调用栈上;以及响应于检测到所述被调用协同例程的恢复:仅将所述被调用协同例程的所述栈帧复制到所述单个线性机器调用栈上,而不将所述单个线性机器调用栈的所有所述能恢复部分复制到所述单个线性机器调用栈上。
【技术特征摘要】
...
【专利技术属性】
技术研发人员:N·M·加夫特,M·托格森,H·J·M·梅杰,N·古斯塔夫松,
申请(专利权)人:微软公司,
类型:发明
国别省市:US
还没有人留言评论。发表了对其他浏览者有用的留言会获得科技券。