Question

J'ai suivi la nouvelle annonce concernant la nouvelle fonctionnalité de async qui sera en c # 5.0. J'ai une compréhension de base du style de passage de continuation et de la transformation du nouveau compilateur c # rend le code comme cet extrait de après Eric Lippert:

async void ArchiveDocuments(List<Url> urls)
{
  Task archive = null;
  for(int i = 0; i < urls.Count; ++i)
  {
    var document = await FetchAsync(urls[i]);
    if (archive != null)
      await archive;
    archive = ArchiveAsync(document);
  }
}

Je sais que certaines langues mettent en œuvre continuations nativement, via un appel avec continuation-courant (callcc), mais je ne comprends pas vraiment comment ça fonctionne ou ce qu'il fait exactement.

Alors, voici la question: si Anders et al. avait décidé de mordre la balle et juste mettre en œuvre callcc en C # 5.0 au lieu du cas spécial async / await, quel serait le look extrait ci-dessus comme?

Était-ce utile?

La solution

Réponse d'origine:

Votre question, si je comprends bien, est « si au lieu de mettre en œuvre « await » spécifiquement pour asynchronisme basée sur les tâches plutôt l'opération de flux de contrôle plus général de l'appel avec-continuation en cours avait été mis en œuvre? »

Eh bien, tout d'abord nous devons penser à ce que « Attendent » fait. "Await" prend une expression de type Task<T>, obtient un awaiter, et appelle la awaiter avec la poursuite en cours:

await FooAsync()

devient efficacement

var task = FooAsync();
var awaiter = task.GetAwaiter();
awaiter.BeginAwait(somehow get the current continuation);

Supposons maintenant que nous avons eu un callcc opérateur qui prend comme argument une méthode, et appelle la méthode avec la poursuite en cours. Cela ressemblerait à ceci:

var task = FooAsync();
var awaiter = task.GetAwaiter();
callcc awaiter.BeginAwait;

En d'autres termes:

await FooAsync()

est rien de plus que

callcc FooAsync().GetAwaiter().BeginAwait;

Est-ce que cela répond à votre question?


Mise à jour # 1:

En tant que commentateur souligne, la réponse ci-dessous suppose le modèle de génération de code de la version « Technology Preview » de la fonction async / vous attendent. Nous générons en fait un code légèrement différent dans la version bêta de la fonctionnalité, bien que logiquement il est le même. La présente codegen est quelque chose comme:

var task = FooAsync();
var awaiter = task.GetAwaiter();
if (!awaiter.IsCompleted)
{
    awaiter.OnCompleted(somehow get the current continuation);
    // control now returns to the caller; when the task is complete control resumes...
}
// ... here:
result = awaiter.GetResult();
// And now the task builder for the current method is updated with the result.

Notez que cela est un peu plus compliqué, et traite le cas où vous « attendez » un résultat qui a déjà été calculé. Il n'y a pas besoin de passer par tous les rigamarole de céder le contrôle à l'appelant et ramasser à nouveau où vous l'avez laissé si le résultat que vous attendez est en fait déjà mises en mémoire cache pour vous là.

Ainsi, la connexion entre « await » et « callcc » est pas tout à fait aussi simple qu'il était dans la version préliminaire, mais il est toujours clair que nous faisons essentiellement un callcc sur la méthode « OnCompleted » du awaiter. Nous ne pas faire le callcc si nous n'avons pas.


Mise à jour # 2:

Comme cette réponse

https://stackoverflow.com/a/9826822/88656

de Timwi indique, la sémantique de l'appel / cc et attendre ne sont pas tout à fait le même; un « vrai » appel / cc nécessite soit que nous « capture » ensemble poursuite d'une méthode y compris sa pile appel tout , ou de façon équivalente que l'ensemble du programme sera réécrite en continuation le style qui passe.

La fonction « await » est plus comme un « appel coopératif / cc »; la poursuite ne pourra capturer « ce qui est le courant méthode de tâche retour sur le point de faire ensuite au point de la glisse vous attendent? » Si le appelant de la méthode de tâche retour va faire intéressant quelque chose une fois la tâche terminée, il est alors libre de vous inscrire la continuation la poursuite de la tâche.

Autres conseils

Je ne suis pas expert en continuations, mais je vais prendre un coup de poignard à expliquer la différence entre async / Attendent et appel / cc. Bien sûr, cette explication suppose que je comprends appel / cc et async / Attendent, que je ne suis pas sûr que je fais. Néanmoins, va ici ...

Avec C # « async », vous dites au compilateur de générer une version spéciale de cette méthode spécifique qui comprend comment mettre en bouteille état de en données tas structure, il peut donc être « retiré de la vraie pile » et repris plus tard. Au sein d'un contexte async, « await » est alors comme « appel / cc » en ce qu'il utilise les objets générés par le compilateur à embouteiller l'état et descendre la « pile réelle » jusqu'à ce que finalise la tâche. Cependant, parce qu'il est le compilateur de réécriture d'un async-méthode qui permet à l'État d'être mis en bouteille, await ne peut être utilisé dans un contexte async.

En cours d'appel / cc de première classe, le moteur d'exécution de langage génère tout le code de telle sorte que la poursuite actuelle peut être mis en bouteille dans une continuation fonction d'appel (mot-clé le rendant inutile async). call / cc agit toujours comme attendent, provoquant l'état de continuité de courant (penser à l'état de la pile) à botled et adoptée en tant que fonction de la fonction appelée. Une façon de tous invocation de la fonction est de faire tas-cadres pour utiliser, au lieu des cadres « pile ». (Parfois dénommé « stackless », comme dans « python stackless » ou de nombreuses implémentations de schéma) Une autre façon est de supprimer toutes les données de la « pile réel » et d'autres choses dans un tas structure de données avant d'appeler la cible d'appel / cc .

Quelques questions délicates peuvent se poser s'il y a des appels à des fonctions externes (pensez DllImport) entremêlés sur la pile. Je soupçonne que c'est la raison pour laquelle ils sont allés avec la async / mise en œuvre vous attendent.

http://www.madore.org/~david/computers/callcc. html


Parce que dans C #, une fonction doit être marqué comme « async » afin d'utiliser ces mécanismes, je me demande si ce mot-clé async deviendra un virus qui se propage à un bon nombre de fonctions dans de nombreuses bibliothèques. Si ça arrive. ils peuvent éventuellement se rendre compte qu'ils devraient mettre en œuvre appel / cc de première classe au niveau VM, au lieu de ce compilateur de réécriture à base async modèle. Seul le temps nous le dira. Cependant, il est certainement un outil utile dans le contexte de l'environnement actuel C #.

Kinda inutile, je dirais. interpréteurs Scheme mettent souvent en œuvre appel / cc avec une machine d'état qui capture état local sur le tas. Ce qui est exactement ce que C # 5.0 (ou plus correctement C # 2.0 iterator de) fait aussi bien. Ils a fait mettre en œuvre appel / cc, l'abstraction, ils sont venus avec est assez élégant.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top