首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何使用call/cc实现c# 5.0中的新异步功能?

如何使用call/cc实现c# 5.0中的新异步功能?
EN

Stack Overflow用户
提问于 2010-11-01 23:20:28
回答 3查看 3K关注 0票数 20

我一直在关注关于c# 5.0中新的async特性的新公告。我对延续传递方式和新的c#编译器对Eric Lippert's post中的代码片段所做的转换有了基本的了解

代码语言:javascript
复制
async void ArchiveDocuments(List<Url> urls)
{
  Task archive = null;
  for(int i = 0; i < urls.Count; ++i)
  {
    var document = await FetchAsync(urls[i]);
    if (archive != null)
      await archive;
    archive = ArchiveAsync(document);
  }
}

我知道有些语言通过带有当前值的调用(callcc)实现了本机延续,但我并不真正理解它是如何工作的,或者它到底做了什么。

所以问题来了:如果Anders等人。决定咬紧牙关,只在c# 5.0中实现callcc,而不是async/await的特殊情况,那么上面的代码片段会是什么样子呢?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2010-11-02 00:42:52

原始答案:

根据我的理解,您的问题是“如果不是专门为基于任务的异步实现"await”,而是实现了更通用的call-with-current- control控制流操作,会怎么样?“

好吧,首先让我们考虑一下"await“是做什么的。"await“接受类型为Task<T>的表达式,获取一个等待器,并使用当前的continuation调用该等待器:

代码语言:javascript
复制
await FooAsync()

变得更加有效

代码语言:javascript
复制
var task = FooAsync();
var awaiter = task.GetAwaiter();
awaiter.BeginAwait(somehow get the current continuation);

现在假设我们有一个运算符callcc,它将一个方法作为其参数,并使用当前的延续调用该方法。它看起来像这样:

代码语言:javascript
复制
var task = FooAsync();
var awaiter = task.GetAwaiter();
callcc awaiter.BeginAwait;

换句话说:

代码语言:javascript
复制
await FooAsync()

只不过是

代码语言:javascript
复制
callcc FooAsync().GetAwaiter().BeginAwait;

这回答了你的问题吗?

更新#1:

正如评论者指出的那样,下面的答案假设了异步/等待功能的“技术预览”版本中的代码生成模式。我们实际上在功能的测试版中生成了略有不同的代码,尽管在逻辑上是相同的。目前的codegen类似于:

代码语言:javascript
复制
var task = FooAsync();
var awaiter = task.GetAwaiter();
if (!awaiter.IsCompleted)
{
    awaiter.OnCompleted(somehow get the current continuation);
    // control now returns to the caller; when the task is complete control resumes...
}
// ... here:
result = awaiter.GetResult();
// And now the task builder for the current method is updated with the result.

请注意,这有点复杂,并且处理您正在“等待”已经计算出的结果的情况。如果你等待的结果实际上已经在内存中为你缓存了,那么就没有必要经历将控制权交给调用者的所有繁琐工作,并从你停止的地方重新开始。

因此,"await“和" callcc”之间的联系并不像在预览版中那样简单,但很明显,我们本质上是在等待器的"OnCompleted“方法上做一个callcc。如果没有必要,我们就不做callcc。

更新#2:

就像这个答案

https://stackoverflow.com/a/9826822/88656

Timwi指出,call/cc和await的语义并不完全相同;“真正的”call/cc要求我们“捕获”方法的整个延续,包括它的整个调用堆栈,或者等效地将整个程序重写为延续传递样式。

"await“特性更像是一个”协作调用/cc“;continuation只捕获”当前的任务返回方法在等待时下一步要做什么?“如果任务返回方法的调用者要在任务完成后做一些有趣的事情,那么它可以自由地将其complete注册为任务的complete。

票数 29
EN

Stack Overflow用户

发布于 2010-12-17 01:36:20

我不是延续方面的专家,但我将尝试解释async/await和call/cc之间的区别。当然,这个解释假设我理解call/cc和async/await,我不确定我是否理解。不管怎么说,这是……

使用C# 'async',你告诉编译器生成特定方法的一个特殊版本,该方法理解如何将它的状态封装到堆数据结构中,这样它就可以“从实际堆栈中移除”,并在以后恢复。在异步上下文中,"await“类似于"call/cc”,因为它使用编译器生成的对象来封装状态,并离开“实际堆栈”,直到任务完成。但是,因为它是对异步方法的编译器重写,允许将状态封存起来,所以await只能在异步上下文中使用。

在第一类call/cc中,语言运行时生成所有代码,以便可以将当前的continuation封装到call-continuation function中(使async关键字变得不必要)。call/cc仍然像await一样,导致当前延续状态(想想堆栈状态)被阻塞,并作为函数传递给被调用的函数。要做到这一点,一种方法是对所有函数调用使用堆框架,而不是“栈”框架。(有时称为“stackless”,如在“stackless python”或许多方案实现中)另一种方法是在调用call/cc目标之前从“真实堆栈”中移除所有数据,并将其填充到堆数据结构中。

如果堆栈中混合了对外部函数(比如DllImport)的调用,可能会出现一些棘手的问题。我怀疑这就是他们使用async/await实现的原因。

http://www.madore.org/~david/computers/callcc.html

因为在C#中,一个函数必须标记为" async“才能使用这些机制,我想知道这个async关键字是否会成为一种病毒,传播到许多库中的许多函数中。如果发生这种情况。他们可能最终意识到他们应该在VM级实现一流的call/cc,而不是这种基于编译器重写的异步模型。只有时间能证明一切。但是,在当前C#环境的上下文中,它无疑是一个有用的工具。

票数 4
EN

Stack Overflow用户

发布于 2010-11-01 23:43:25

我要说的是有点没意思。模式解释器通常使用状态机实现call/cc,该状态机将本地状态捕获到堆上。这也正是C# 5.0 (或者更准确地说,C# 2.0的迭代器)所做的事情。他们确实实现了call/cc,他们提出的抽象非常优雅。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4070237

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档