Je vais répondre à vos questions ci-dessous, mais vous faites bien de simplement lire mes nombreux articles sur la façon dont nous avons conçu le rendement et l'attendent.
https://blogs.msdn.microsoft.com/ericlippert/tag/continuation-passing-style/
https://blogs.msdn.microsoft.com/ericlippert/tag/iterators/
https://blogs.msdn.microsoft.com/ericlippert/tag/async/
Certains de ces articles ne sont pas à jour, le code généré est différent dans beaucoup de façons. Mais ce sera certainement vous donner une idée de comment cela fonctionne.
Aussi, si vous ne comprenez pas comment les lambdas sont générés de fermeture de classes, de comprendre que la première. Vous ne voulez pas faire des têtes ou queues de async si vous n'avez pas lambdas vers le bas.
Lorsqu'une attendent est atteint, comment fonctionne le moteur d'exécution de savoir ce morceau de code doit s'exécuter prochaine?
await
est généré sous la forme:
if (the task is not completed)
assign a delegate which executes the remainder of the method as the continuation of the task
return to the caller
else
execute the remainder of the method now
C'est principalement ça. Attendent est juste une fantaisie de retour.
Comment savoir quand il peut la reprendre là où il l'a laissée, et comment faut-il rappeler où?
Eh bien, comment faites-vous sans attendre? Lorsque la méthode foo appels de méthode bar, d'une certaine manière, nous nous souvenons comment obtenir de nouveau au milieu de foo, avec tous les habitants de l'activation de foo intact, n'importe quel bar.
Vous savez comment c'est fait en assembleur. Une activation de l'enregistrement pour les foo est poussé sur la pile; il contient les valeurs de la population locale. Au moment de l'appel, l'adresse de retour dans foo est poussé sur la pile. Lorsque la barre est fait, le pointeur de pile et le pointeur d'instruction sont remis à l'endroit où ils doivent être et toto continue à aller de l'endroit où il l'avait laissé.
La poursuite de l'attendent exactement la même, sauf que l'enregistrement est mis sur le tas, pour la raison évidente que la séquence d'activations ne forme pas une pile.
Le délégué qui attendent donne comme la continuation de la tâche contient (1) un nombre qui est l'entrée dans une table de recherche qui donne le pointeur d'instruction que vous avez besoin pour exécuter prochaine, et (2) toutes les valeurs de locaux et temporaires.
Il y a quelques autres engins de là; par exemple, dans .NET, il est illégal de direction dans le milieu d'un bloc try, de sorte que vous ne pouvez pas simplement coller l'adresse de code à l'intérieur d'un bloc try dans la table. Mais ce sont de comptabilité de détails. Sur le plan conceptuel, l'activation de l'enregistrement est simplement déplacé sur le tas.
Qu'advient-il de la pile d'appel en cours, fait-il sauvé en quelque sorte?
Les informations pertinentes dans le courant de l'activation n'est jamais mis sur la pile en premier lieu; il est alloué au large de la tas à partir de l'obtenir-aller. (Eh bien, les paramètres formels sont passés sur la pile ou dans les registres normalement et ensuite copié dans un segment de mémoire lorsque l'emplacement de la méthode commence.)
L'activation des dossiers d'appelants ne sont pas stockées; await va probablement revenir à eux, rappelez-vous, donc ils vont être traitées normalement.
Notez que c'est un germane différence entre la simplification de la continuation passing style de l'attendent, et la vraie call-with-current-poursuite des structures que vous voyez dans les langues comme le Régime. Dans ces langues, l'ensemble de la poursuite, y compris la poursuite de retour dans les appelants est capturé par appel-cc.
Que faire si l'appel de la méthode rend les autres appels de méthode avant qu'il attend, pourquoi ne pas la pile écrasés?
Ces appels de méthode de retour, et ainsi de leur activation, les enregistrements ne sont plus sur la pile, au point de l'attendre.
Et comment sur terre serait le moteur d'exécution travailler son chemin à travers tout cela, dans le cas d'une exception et une pile de vous détendre?
Dans le cas d'une exception non interceptée, l'exception est interceptée, stockée à l'intérieur de la tâche, et re-levée lorsque la tâche que le résultat de l'extraction.
Rappelez-vous tout ce que la comptabilité je l'ai mentionné précédemment? Obtenir exception de la sémantique à droite a été une énorme douleur, permettez-moi de vous le dire.
Lorsque le rendement est atteint, comment fonctionne le moteur d'exécution de garder la trace du point où les choses doivent être ramassés? Comment est itérateur de l'état est-elle préservée?
De la même manière. L'état des locaux est déplacé sur le tas, et un nombre représentant l'instruction à qui MoveNext
devrait reprendre la prochaine fois, il est appelé est stocké avec les habitants.
Et encore, il y a un tas de matériel dans un itérateur bloc pour s'assurer que les exceptions sont traitées correctement.