【问题标题】:async ctp recursion异步ctp递归
【发布时间】:2011-09-20 19:35:00
【问题描述】:

我第一次玩 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 调用自己。在经典的非异步世界中,这会导致堆栈溢出。我认为异步方法不是这种情况,但我想确定,一种或另一种方式。有人吗?

【问题讨论】:

    标签: c# recursion async-ctp


    【解决方案1】:

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

    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。调用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”一次只能在堆栈上一次。

    【讨论】:

    • 好。我也有同样的怀疑,但很高兴能详细了解该构造的机制。感谢您提供如此全面的答案(像往常一样!)。
    • @spender:不客气!享受 CTP,如果您有任何问题、cmets、疑虑、表扬、建设性批评等,请将它们发布到 Async CTP 论坛。我们的项目经理每天都会阅读这些内容并收集用户对该功能的反馈,这对我们非常有帮助。
    猜你喜欢
    • 2012-06-04
    • 1970-01-01
    • 2011-12-10
    • 1970-01-01
    • 1970-01-01
    • 2011-08-19
    • 2011-10-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多