看完Eric Lippert’s answer我的印象是 await
和 call/cc
几乎是同一枚硬币的两面,最多只是句法上的差异。然而,在尝试实际实现时 call/cc
在 C# 5 中,我遇到了一个问题:要么我误解了 call/cc(这很有可能),要么 await 只是 让人想起 call/cc。
考虑这样的伪代码:
function main:
foo();
print "Done"
function foo:
var result = call/cc(bar);
print "Result: " + result;
function bar(continuation):
print "Before"
continuation("stuff");
print "After"
如果我对 call/cc 的理解是正确的,那么应该打印:
Before
Result: stuff
Done
至关重要的是,当继续被调用时,程序状态与调用历史一起恢复,所以foo
返回到 main
永远不会回到bar
.
但是,如果使用 await
实现在 C# 中,调用延续不会恢复此调用历史记录。 foo
返回到 bar
,并且没有办法(我可以看到)await
可用于使正确的通话记录成为延续的一部分。
请解释:我是不是完全误解了call/cc
的操作,或者是 await
只是与call/cc
不太一样?
既然我知道了答案,我不得不说有充分的理由认为它们非常相似。考虑上面的程序在伪 C#-5 中的样子:
function main:
foo();
print "Done"
async function foo:
var result = await(bar);
print "Result: " + result;
async function bar():
print "Before"
return "stuff";
print "After"
因此,虽然 C# 5 风格从不给我们一个延续对象来传递一个值,但总的来说相似度是相当惊人的。除了这次很明显“After”永远不会被调用,这与 true-call/cc 示例不同,这是另一个喜欢 C# 并赞扬其设计的原因!
最佳答案
await
确实和call/cc
不太一样。
您正在考虑的那种完全基础的 call/cc
确实必须保存和恢复整个调用堆栈。但是 await
只是一个编译时转换。它执行类似的操作,但不使用真实 调用堆栈。
假设您有一个包含 await 表达式的异步函数:
async Task<int> GetInt()
{
var intermediate = await DoSomething();
return calculation(intermediate);
}
现在假设您通过 await
自身 调用的函数包含一个 await
表达式:
async Task<int> DoSomething()
{
var important = await DoSomethingImportant();
return un(important);
}
现在想想当 DoSomethingImportant()
完成并且其结果可用时会发生什么。控制返回到 DoSomething()
。然后 DoSomething()
完成,然后会发生什么?控制返回到 GetInt()
。如果 GetInt()
在调用堆栈上,则行为与 完全相同。但事实并非如此;您必须在希望以这种方式模拟的每个 调用中使用await
。因此,调用堆栈被提升到在等待程序中实现的元调用堆栈。
顺便说一句,yield return
也是如此:
IEnumerable<int> GetInts()
{
foreach (var str in GetStrings())
yield return computation(str);
}
IEnumerable<string> GetStrings()
{
foreach (var stuff in GetStuffs())
yield return computation(stuff);
}
现在如果我调用 GetInts()
,我得到的是一个对象,它封装了 GetInts()
的当前执行状态(以便调用 MoveNext ()
在它停止的地方恢复操作)。此对象本身包含遍历 GetStrings()
并在 that 上调用 MoveNext()
的迭代器。因此,真正的 调用堆栈被对象层次结构所取代,这些对象层次结构每次都通过对下一个内部对象的 MoveNext()
的一系列调用来重新创建正确的调用堆栈。
关于C# 等待与延续 : not quite the same?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9826535/