我和async CTP的第一场比赛已经进行了15分钟……(很好)。
这是我拼凑的一个非常简单的服务器:
internal class Server
{
private HttpListener listener;
public Server()
{
listener = new HttpListener();
listener.Prefixes.Add("http://*:80/asynctest/");
listener.Start();
Go();
}
async void Go()
{
HttpListenerContext context = await listener.GetContextAsync();
Go();
using (var httpListenerResponse = context.Response)
using (var outputStream = httpListenerResponse.OutputStream)
using (var sw = new StreamWriter(outputStream))
{
await sw.WriteAsync("hello world");
}
}
}可以看出,异步方法Go调用自身。在传统的非异步环境中,这将导致堆栈溢出。我假设异步方法不是这样的,但我想确认一下,不管是哪种方式。有没有人?
发布于 2011-06-23 11:24:07
让我们把它分解成更简单的东西:
async static void Go()
{
await Something();
Go();
await SomethingElse();
}编译器是如何处理这个问题的?
基本上,这变成了类似于下面的草图:
class HelperClass
{
private State state = STARTSTATE;
public void DoIt()
{
if (state == STARTSTATE) goto START;
if (state == AFTERSOMETHINGSTATE) goto AFTERSOMETHING;
if (state == AFTERSOMETHINGELSESTATE) goto AFTERSOMETHINGELSE;
START:
{
state = AFTERSOMETHINGSTATE;
var awaiter = Something().MakeAnAwaiter();
awaiter.WhenDoneDo(DoIt);
return;
}
AFTERSOMETHING:
{
Go();
state = AFTERSOMETHINGELSESTATE;
var awaiter = SomethingElse().MakeAnAwaiter();
awaiter.WhenDoneDo(DoIt);
return;
}
AFTERSOMETHINGELSE:
return;
}
static void Go()
{
var helper = new HelperClass();
helper.DoIt();
}现在,您需要记住的是,当每个异步操作完成时,消息循环将再次调用"DoIt“(当然是在帮助器的适当实例上)。
那么会发生什么呢?把它弄清楚。你第一次调用Go。这使得帮助器成为第一个,并调用DoIt。这将调用某个(),获取一个任务,为该任务创建一个等待器,告诉该等待器“完成后,调用helper1.DoIt”,然后返回。
十分之一秒后,任务完成,消息循环调用helper1的DoIt。Helper1的状态是AFTERSOMETHINGSTATE,所以我们获取goto并调用Go。这将生成helper2并对其调用DoIt。这将调用Something(),获取一个任务,为该任务创建一个等待器,告诉该等待器“完成后,在helper2上调用DoIt”,并将控制权返回给helper1的DoIt。它调用SomethingElse,为该任务创建一个等待器,并告诉它“当你做完其他事情时,调用helper1的DoIt”。然后它会返回。
现在我们有两个未完成的任务,堆栈上没有代码。其中一项任务将首先完成。假设SomethingElse任务首先完成。消息循环调用helper1.DoIt(),该函数立即返回。Helper1现在是垃圾了。
稍后,消息循环调用helper2.DoIt(),并转移到AFTERSOMETHING。现在调用Go(),它创建了helper3...
所以不,这里没有无限的递归。每次Go执行时,它都会异步启动某个东西(),然后返回给调用者。对"something“之后的东西的调用稍后会发生。"Go“一次只能在堆栈上出现一次。
https://stackoverflow.com/questions/6447626
复制相似问题