2011-06-22 9 views
5

Je suis environ 15 minutes dans mon premier jeu avec le async CTP ... (nice).async ctp recursion

est ici un serveur très simple, je l'ai bricolé:

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"); 
     } 
    } 
} 

Comme on le voit, la méthode async Go appelle lui-même. Dans le monde non-asynchrone classique, cela provoquerait un débordement de pile. Je suppose que ce n'est pas le cas avec une méthode asynchrone, mais je voudrais être sûr, d'une façon ou d'une autre. N'importe qui?

Répondre

13

Brisons le décomposer en quelque chose de plus simple:

async static void Go() 
{ 
    await Something(); 
    Go(); 
    await SomethingElse(); 
} 

Comment l'accord du compilateur avec cela?

En gros, cela devient quelque chose comme ce croquis:

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(); 
    } 

Maintenant, tout ce que vous avez à retenir est que lorsque chaque fin de l'opération asynchrone, « DoIt » est prévu pour être appelé à nouveau par la boucle de message (sur le approprié exemple de l'assistant bien sûr).

Alors, que se passe-t-il? Travaillez. Vous appelez Go pour la première fois. Cela rend l'assistant numéro un et appelle DoIt. Cela appelle Quelque chose(), récupère une tâche, fait une attente pour cette tâche, indique à l'appelant "quand vous avez fini, appelle helper1.DoIt" et retourne. Un dixième de seconde plus tard, la tâche se termine et la boucle de message appelle DoIt de helper1. L'état de helper1 est AFTERSOMETHINGSTATE, donc nous prenons le goto et appelons Go. Cela fait helper2 et appelle DoIt à ce sujet. Cela appelle Quelque chose(), récupère une tâche, fait une attente pour cette tâche, indique à l'appelant "quand vous avez fini, appelle DoIt sur helper2" et renvoie le contrôle au bouton d'aide de helper1. Cela appelle SomethingElse, fait une attente pour cette tâche et lui dit "quand vous avez fini de faire autre chose, appelez le DoIt de helper1". Il revient ensuite.

Maintenant, nous avons deux tâches en attente et aucun code sur la pile. Une des tâches se terminera en premier. Supposons que la tâche SomethingElse se termine en premier. La boucle de message appelle helper1.DoIt(), qui renvoie immédiatement. Helper1 est maintenant poubelle. Plus tard, la boucle de message appelle helper2.DoIt() et les branches à AFTERSOMETHING. Maintenant, Go() est appelé, ce qui crée helper3 ...

Donc non, il n'y a pas de récursion illimitée ici. Chaque fois que Go s'exécute, il s'exécute de façon asynchrone à partir de Something(), puis revient à son appelant. L'appel à la substance après "quelque chose" se produit plus tard. "Go" est seulement sur la pile une fois à la fois.

+0

Bonne. Je m'en doutais autant, mais il est bon de voir en détail la mécanique de la construction. Merci pour une réponse aussi complète (comme d'habitude!). – spender

+0

@spender: De rien! Profitez du CTP, et s'il vous plaît, si vous avez des questions, des commentaires, des préoccupations, des éloges, des critiques constructives, etc., postez-les sur le forum Async CTP. Nous avons des gestionnaires de programme qui lisent tous les jours et recueillent les commentaires des utilisateurs sur la fonctionnalité qui nous est très utile. –