OK, voici mon point de vue à ce sujet.
Il y a quelque chose appelé coroutines qui est connu depuis des décennies. ("Knuth and Hopper" -class "pendant des décennies") Ce sont des généralisations de sous - programmes , dans la mesure où non seulement ils obtiennent et relâchent le contrôle au début de la fonction et la déclaration de retour, mais ils le font également à des points spécifiques ( points de suspension ). Un sous-programme est une coroutine sans point de suspension.
Ils sont simples à mettre en œuvre avec des macros C, comme le montre le document suivant sur les "protothreads". ( http://dunkels.com/adam/dunkels06protothreads.pdf ) Lisez-le. J'attendrai...
L'essentiel, c'est que les macros créent un gros switch
et une case
étiquette à chaque point de suspension. À chaque point de suspension, la fonction stocke la valeur de l' case
étiquette immédiatement suivante , afin qu'elle sache où reprendre l'exécution la prochaine fois qu'elle sera appelée. Et il rend le contrôle à l'appelant.
Cela se fait sans modifier le flux apparent de contrôle du code décrit dans le "protothread".
Imaginez maintenant que vous avez une grande boucle appelant à tour de rôle toutes ces "protothreads" et que vous obtenez simultanément l'exécution de "protothreads" sur un seul thread.
Cette approche présente deux inconvénients:
- Vous ne pouvez pas conserver l'état dans les variables locales entre les reprises.
- Vous ne pouvez pas suspendre le "protothread" à partir d'une profondeur d'appel arbitraire. (tous les points de suspension doivent être au niveau 0)
Il existe des solutions de contournement pour les deux:
- Toutes les variables locales doivent être remontées au contexte de la protothread (contexte qui est déjà requis par le fait que la protothread doit stocker son prochain point de reprise)
- Si vous sentez que vous avez vraiment besoin d'appeler une autre protothread à partir d'une protothread, "générez" une enfant protothread et suspendez jusqu'à la fin de l'enfant.
Et si vous aviez le support du compilateur pour faire le travail de réécriture que font les macros et la solution de contournement, vous pouvez simplement écrire votre code de prototread comme vous le souhaitez et insérer des points de suspension avec un mot clé.
Et voici ce async
et await
sont tous sur: la création (Stackless) coroutines.
Les coroutines en C # sont réifiées en tant qu'objets de classe (générique ou non générique) Task
.
Je trouve ces mots clés très trompeurs. Ma lecture mentale est:
async
comme "suspensible"
await
comme "suspendre jusqu'à la fin de"
Task
comme "futur ..."
Maintenant. Avons-nous vraiment besoin de marquer la fonction async
? En plus de dire qu'il devrait déclencher les mécanismes de réécriture de code pour faire de la fonction une coroutine, il résout certaines ambiguïtés. Considérez ce code.
public Task<object> AmIACoroutine() {
var tcs = new TaskCompletionSource<object>();
return tcs.Task;
}
En supposant que ce async
n'est pas obligatoire, est-ce une coroutine ou une fonction normale? Le compilateur devrait-il le réécrire en coroutine ou non? Les deux pourraient être possibles avec une sémantique éventuelle différente.