我第一次玩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会自行调用。在经典的非异步世界中,这会导致堆栈溢出。我假设异步方法不是这种情况,但我想确定一种方法还是另一种方法。任何人?

最佳答案

让我们将其分解为更简单的内容:

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。那使 helper 成为第一,并调用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”一次只能出现在堆栈中一次。

关于c# - 异步ctp递归,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/6447626/

10-11 02:55