C# await vs continuations: not quite the same?

前端 未结 1 1481
长情又很酷
长情又很酷 2020-12-28 19:12

After reading Eric Lippert’s answer I got the impression that await and call/cc are pretty much two sides of the same coin, with at most syntactic

1条回答
  •  暖寄归人
    2020-12-28 19:17

    await is indeed not quite the same as call/cc.

    The kind of totally fundamental call/cc that you are thinking of would indeed have to save and restore the entire call stack. But await is just a compile-time transformation. It does something similar, but not using the real call stack.

    Imagine you have an async function containing an await expression:

    async Task GetInt()
    {
        var intermediate = await DoSomething();
        return calculation(intermediate);
    }
    

    Now imagine that the function you call via await itself contains an await expression:

    async Task DoSomething()
    {
        var important = await DoSomethingImportant();
        return un(important);
    }
    

    Now think about what happens when DoSomethingImportant() finishes and its result is available. Control returns to DoSomething(). Then DoSomething() finishes and what happens then? Control returns to GetInt(). The behaviour is exactly as it would be if GetInt() were on the call stack. But it isn’t really; you have to use await at every call that you want simulated this way. Thus, the call stack is lifted into a meta-call-stack that is implemented in the awaiter.

    The same, incidentally, is true of yield return:

    IEnumerable GetInts()
    {
        foreach (var str in GetStrings())
            yield return computation(str);
    }
    
    IEnumerable GetStrings()
    {
        foreach (var stuff in GetStuffs())
            yield return computation(stuff);
    }
    

    Now if I call GetInts(), what I get back is an object that encapsulates the current execution state of GetInts() (so that calling MoveNext() on it resumes operation where it left off). This object itself contains the iterator that is iterating through GetStrings() and calls MoveNext() on that. Thus, the real call stack is replaced by a hierarchy of objects which recreate the correct call stack each time via a series of calls to MoveNext() on the next inner object.

    0 讨论(0)
提交回复
热议问题