如何使用call / cc实现c#5.0中的新异步功能?
-
28-09-2019 - |
题
我一直在关注有关c#5.0中新的async
功能的新公告。我对延续传递样式和新的c#编译器对来自埃里克·利珀特的帖子:
通用标签
我知道有些语言是通过当前调用继续(callcc
)来本地实现延续的,但是我并不真正了解它的工作原理或确切的作用。
这就是问题:如果安德斯(Anders)等人。已经决定硬着头皮,只在c#5.0中实现了callcc
,而不是async
/ await
特殊情况,那么上面的代码片段是什么样的?
解决方案
原始答案:
据我所知,您的问题是“如果不是专门针对基于任务的异步实施“等待”,而是实施了更通用的“随电流连续调用”控制流操作,该怎么办?”
好吧,首先让我们考虑一下“等待”的作用。 “ await”采用Task<T>
类型的表达式,获取一个等待者,并使用当前继续符来调用该等待者:
通用标签
有效地发挥作用 通用标签
现在假设我们有一个运算符callcc
,它以一个方法作为参数,并使用当前的延续来调用该方法。看起来像这样:
通用标签
换句话说: 通用标签
无非是 通用标签
这能回答您的问题吗?
更新#1:
正如评论者所指出的那样,以下答案假定来自异步/等待功能的“技术预览”版本的代码生成模式。实际上,我们在功能的Beta版中生成的代码略有不同,尽管从逻辑上来说是相同的。当前的代码生成类似于: 通用标签
请注意,这有些复杂,可以处理“等待”已经计算出的结果的情况。如果等待的结果实际上已经存储在内存中,则无需遍历将控制权交给调用者并在接听的地方重新接管的所有繁琐操作。
因此,“ await”和“ callcc”之间的联系并不像预览版中那么简单,但是仍然很清楚,我们实际上是在等待者的“ OnCompleted”方法上进行一个callcc。如果没有必要,我们只是不执行callcc。
更新#2:
此答案
https://stackoverflow.com/a/9826822/88656
从Timwi指出,call / cc和await的语义并不完全相同。 “真正的”调用/ cc要求我们“捕获”方法的整个延续,包括整个调用栈,或者等效地,整个程序都应重写为延续传递风格。
“等待”功能更像是“合作呼叫/抄送”;续集仅捕获“在等待时下一步下一步要执行的当前任务返回方法是什么?”如果任务完成后返回任务的 caller 会做一些有趣的事情,那么可以自由地将其延续签名为任务的延续。
其他提示
我不是专家,但我会解释一下async / await和call / cc之间的区别。当然,此说明假定我理解call / cc和async / await,但我不确定这样做。尽管如此,这里...
使用C#'async',您要告诉编译器生成该特定方法的特殊版本,该特定版本了解如何将其状态瓶入堆数据结构,以便可以将其“从真实堆栈中删除”,并且稍后恢复。在异步上下文中,“ await”类似于“ call / cc”,因为它使用编译器生成的对象来封装状态并脱离“实际堆栈”,直到任务完成。但是,由于是编译器重写了异步方法,该状态方法才允许瓶装状态,所以只能在异步上下文中使用等待。
在一流的呼叫/ cc中,语言运行时会生成所有代码,以便可以将当前的继续操作充入呼叫继续功能(使async关键字不必要)。 call / cc仍然像await一样,导致当前继续状态(认为是堆栈状态)被破坏并作为函数传递给被调用函数。一种实现方法是使用堆帧进行 all 函数调用,而不是使用“堆栈”帧。 (有时被称为“无堆栈”,例如在“无堆栈python”或许多方案实现中)。另一种方法是在调用call / cc目标之前从“实际堆栈”中删除所有数据并将其填充到堆数据结构中。
如果在堆栈上混合了对外部函数的调用(例如DllImport),则会出现一些棘手的问题。我怀疑这是他们采用异步/等待实现的原因。
http://www.madore.org/~david/computers/callcc。 html
由于在C#中,必须将某个函数标记为“异步”才能使用这些机制,所以我想知道此async关键字是否会成为病毒,并传播到许多库中的许多函数中。如果发生这种情况。他们可能最终意识到他们应该在VM级别上实现一流的call / cc,而不是这种基于编译器重写的异步模型。只有时间证明一切。但是,在当前C#环境中,它无疑是一个有用的工具。
我说金达毫无意义。方案解释器通常使用状态机来实现call / cc,该状态机将本地状态捕获到堆中。Cem 5.0(或更正确地说是C#2.0的迭代器)的功能是恰好。他们 did 实现了call / cc,他们想到的抽象非常优雅。