gpt4 book ai didi

C# 等待与延续 : not quite the same?

转载 作者:可可西里 更新时间:2023-11-01 08:06:15 30 4
gpt4 key购买 nike

看完Eric Lippert’s answer我的印象是 awaitcall/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/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com