Async recursion. Where is my memory actually going?

后端 未结 2 1034
伪装坚强ぢ
伪装坚强ぢ 2021-01-12 08:12

This is asked more out of curiosity than with regards to any real-world problem.

Consider the following code:

void Main()
{
    FAsync().Wait();
}

         


        
2条回答
  •  孤街浪徒
    2021-01-12 08:39

    Good question.

    The stack is the reification of continuation. Continuation is, simply, information about what the program is going to do next. In a traditional non-async environment, this is represented as a return address on the stack; when the method returns it looks at the stack and branches to the return address. The stack also has information on it about what the values of the local variables are at the point where the continuation picks up.

    In an async situation all that information is stored on the heap. A task contains a delegate which is invoked when the task is completed. The delegate is bound to an instance of a "closure" class which contains fields for any local variables or other state. And of course the tasks are themselves heap objects.

    You might wonder: if it is the case that the continuation is a delegate which is invoked when the task completes, how then is the code which completes the task not on the call stack at the point where the completion is executed? The task can choose to invoke the continuation delegate by posting a windows message, and when the message loop processes the message, it does the invocation. So the invocation is then at the "top" of the stack, where the message loop typically sits. (The precise details of the invocation strategy used for the continuation depend on the context in which the task is created; see a more advanced guide to the task parallel library for the details.)

    A good introductory article on how this all works can be found here:

    https://msdn.microsoft.com/en-us/magazine/hh456403.aspx

    A few of the details have changed since Mads wrote that article but the ideas are sound. (i3arnon's answer illustrates how this evolved; in Mads's article everything goes on the heap, but this turns out to produce excess garbage in some scenarios. A more sophisticated codegen allows us to keep some of the information on the stack. Understanding that distinction is not necessary to see how the continuations are logically represented.)

    It's an entertaining and enlightening exercise to take your program and actually draw out all the delegates and tasks that are created, and what the references are between them. Give it a shot!

提交回复
热议问题