异步递归.我的记忆实际上在哪里? [英] Async recursion. Where is my memory actually going?

查看:54
本文介绍了异步递归.我的记忆实际上在哪里?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是出于好奇而不是针对任何现实问题的要求.

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

考虑以下代码:

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

async Task FAsync()
{
    await Task.Yield();
    await FAsync();
}

在同步世界中,这最终会导致堆栈溢出.

In the synchronous world, this would ultimately cause a stackoverflow.

在异步世界中,这只会消耗大量内存(我认为这与我可能会松散地称为异步堆栈"的东西有关?)

In the async world, this simply consumes a lot of memory (which I assume is related to something that I might loosely call the "asynchronous stack"?)

这些数据究竟是什么?如何保存?

What precisely is this data, and how is it held?

推荐答案

好问题.

堆栈是 continuation 的具体化.简而言之,连续性是有关程序接下来将要执行的操作的信息.在传统的非异步环境中,这表示为堆栈上的返回地址.方法返回时,它会查看堆栈并跳转到返回地址.堆栈上还包含有关在续集开始时局部变量的值的信息.

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.

您可能会想:如果是在任务完成时调用的延续是委托,那么完成任务的代码又不在调用堆栈上的 上呢?完成在哪里执行?该任务可以选择通过发布Windows消息来调用延续委托,并且当消息循环处理该消息时,它将进行调用.因此,调用位于消息循环通常所在的堆栈的顶部". (用于继续的调用策略的确切细节取决于创建任务的上下文;有关详细信息,请参见任务并行库的更高级指南.)

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

自Mads撰写该文章以来,一些细节发生了变化,但想法是正确的. (i3arnon的答案说明了它是如何演变的;在Mads的文章中,所有内容都在堆上,但是在某些情况下,这会产生过多的垃圾.更复杂的代码生成器使我们能够将某些信息保留在堆栈中.了解这种区别并不是有必要了解延续在逻辑上是如何表示的.)

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!

这篇关于异步递归.我的记忆实际上在哪里?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆