异步ctp递归

spe*_*der 5 c# recursion async-ctp

我使用异步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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

可以看出,异步方法Go调用自身.在经典的非异步世界中,这会导致堆栈溢出.我认为这不是异步方法的情况,但我想确定,不管怎样.任何人?

Eri*_*ert 13

让我们把它分解成更简单的东西:

async static void Go()
{
    await Something();
    Go();
    await SomethingElse();
}
Run Code Online (Sandbox Code Playgroud)

编译器如何处理这个问题?

基本上这就像这个草图:

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();
    }
Run Code Online (Sandbox Code Playgroud)

现在你需要记住的是,当每个异步操作完成时,"DoIt"被安排由消息循环再次调用(当然,在帮助器的适当实例上).

那会发生什么?解决它.你第一次给Go打电话.这使助手排名第一,并调用DoIt.这会调用Something(),返回任务,为该任务做一个等待,告诉等待者"当你完成时,调用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"一次只能在堆栈上进行一次.