Question

Je ne vois pas la différence entre C # 's (et VB de) Nouvelles fonctionnalités async et .NET 4.0 Task Parallel Library . Prenons, par exemple, le code d'Eric Lippert d'ici :

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

Il semble que le mot-clé await sert à deux fins différentes. La première occurrence (FetchAsync) semble signifier, « Si cette valeur est utilisée plus tard dans la méthode et sa tâche n'est pas terminée, attendre qu'il se termine avant de continuer. » la deuxième instance (archive) semble signifier, « Si cette tâche est pas encore terminé, attente en ce moment jusqu'à ce qu'il soit terminé. » Si je me trompe, vous me corrigerez .

pourrait pas aussi être facilement écrit comme ça?

void ArchiveDocuments(List<Url> urls) {
    for(int i = 0; i < urls.Count; ++i) {
        var document = FetchAsync(urls[i]);       // removed await
        if (archive != null)
            archive.Wait();                       // changed to .Wait()
        archive = ArchiveAsync(document.Result);  // added .Result
    }
}

Je l'ai remplacé le premier await avec un Task.Result où la valeur est réellement nécessaire, et le second avec await Task.Wait(), où l'attente se produit réellement. La fonctionnalité est (1) déjà mis en œuvre, et (2) beaucoup plus proche sémantiquement à ce qui se passe réellement dans le code.

Je ne réalise qu'une méthode de async est réécrite sous la forme d'une machine d'état, semblable à itérateurs, mais je ne vois pas aussi ce avantages que cela. Tout code qui nécessite un autre thread pour fonctionner (telles que le téléchargement), il faudra encore un autre thread, et tout code qui ne (comme la lecture d'un fichier) pourrait encore utiliser le TPL à travailler avec un seul fil.

Il me manque évidemment quelque chose d'énorme ici; L'aide peut me comprendre à personne cela un peu mieux?

Était-ce utile?

La solution

Je pense que le malentendu se pose ici:

  

Il semble que le mot clé await sert à deux fins différentes. La première occurrence (FetchAsync) semble signifier: « Si cette valeur est utilisée plus tard dans la méthode et sa tâche n'est pas terminée, attendre qu'il se termine avant de continuer. » La deuxième instance (archives) semble signifier: « Si cette tâche est pas encore terminée, attendez en ce moment jusqu'à ce qu'il soit terminé. » Si je me trompe, s'il vous plaît me corriger.

Ceci est en fait tout à fait incorrect. Ces deux éléments ont la même signification.

Dans votre premier cas:

var document = await FetchAsync(urls[i]);

Qu'est-ce qui se passe ici, est que le temps d'exécution dit « Commencez à appeler FetchAsync, puis retourner le point d'exécution en cours au thread appelant cette méthode. » Il n'y a pas « d'attente » ici - au lieu, l'exécution revient au contexte de synchronisation d'appel, et les choses à garder barattage. À un certain moment dans l'avenir, la tâche de FetchAsync complétera, et à ce moment-là, ce code reprend le contexte de synchronisation de thread appelant, et l'instruction suivante (attribution de la variable de document) se produira.

L'exécution se poursuivra jusqu'à ce que le deuxième appel await - à ce moment, la même chose se produira - si le Task<T> (archive) n'est pas terminée, l'exécution sera publié dans le contexte d'appel - sinon, l'archive sera ensemble .

Dans le second cas, les choses sont très différentes - ici, vous bloquez explicitement, ce qui signifie que le contexte de synchronisation d'appel ne sera jamais obtenir une chance d'exécuter un code jusqu'à ce que l'ensemble de votre finalise la méthode. Certes, il y a encore asynchronisme, mais le asynchronisme est entièrement contenue dans ce bloc de code -. Pas en dehors de code de ce code collé qui va se passer sur ce fil jusqu'à ce que tous vos finalise code

Autres conseils

Il y a une énorme différence:

blocs Wait(), await ne bloque pas. Si vous exécutez la version async de ArchiveDocuments() sur votre fil de l'interface graphique, l'interface graphique reste réactif alors que les opérations et l'archivage sont allant chercher en cours d'exécution. Si vous utilisez la version TPL avec Wait(), votre interface graphique sera bloquée.

Notez que async parvient à le faire sans introduire de discussions - au point de la await, le contrôle est tout simplement retourné à la boucle de message. Une fois que la tâche a attendu terminée, le reste de la méthode (suite) est sur la boucle en file d'attente de messages et le fil de l'interface graphique continuera de tourner ArchiveDocuments où il l'avait laissé.

Anders il se résumait à une réponse très succincte dans la Manche 9 Interview en direct qu'il a fait. Je le recommande fortement

Les nouveaux mots-clés Async et attendez vous permettent de orchestrer concurrency dans vos applications. Ils ne présentent pas réellement de concurrence à votre application.

TPL et plus particulièrement la tâche est une façon vous pouvez utiliser pour effectuer réellement des opérations en même temps. Le nouveau mot-clé et async await vous permettent de Nouveau ces opérations simultanées dans un « synchrone » ou « linéaire » mode.

Vous pouvez toujours écrire un flux linéaire de contrôle dans vos programmes alors que le calcul réel peut ou ne peut pas se produire en même temps. Lorsque le calcul ne se produit en même temps, et await async vous permettent de Nouveau ces opérations.

La capacité de transformer le flux de programme de contrôle dans une machine d'état est ce qui rend ces nouveaux mots-clés intresting. Pensez-y comme le contrôle cédant , plutôt que des valeurs.

Consultez ce canal 9 vidéo de Anders parler de la nouvelle fonction.

Le problème ici est que la signature de ArchiveDocuments est trompeur. Il a un retour explicite de void mais vraiment le retour est Task. Pour moi, implique vide synchrone il n'y a aucun moyen de « attendre » à la fin. Pensez à la signature alternative de la fonction.

async Task ArchiveDocuments(List<Url> urls) { 
  ...
}

Pour moi quand il est écrit de cette façon, la différence est beaucoup plus évidente. La fonction ArchiveDocuments n'est pas un qui complète de manière synchrone mais terminer plus tard.

L'appel à FetchAsync() bloque encore jusqu'à ce qu'il complète (sauf si une déclaration dans les appels await?) La clé est que le contrôle est renvoyé à l'appelant (car la méthode ArchiveDocuments lui-même est déclarée comme async). Ainsi, l'appelant peut heureusement continuer à traiter la logique de l'interface utilisateur, répondre à des événements, etc.

Lorsque FetchAsync() finalise, il interrompt l'appelant pour terminer la boucle. Il frappe ArchiveAsync() et les blocs, mais ArchiveAsync() probablement crée une nouvelle tâche, commence, et retourne la tâche. Cela permet à la seconde boucle commence, alors que la tâche est en train de traiter.

La seconde boucle frappe FetchAsync() et bloque, de rendre le contrôle à l'appelant. Lorsque FetchAsync() finalise, il interrompt à nouveau l'appelant de poursuivre le traitement. Il frappe alors await archive, qui retourne le contrôle à l'appelant jusqu'à ce que le Task créé en boucle 1 est terminée. Une fois cette tâche terminée, l'appelant est à nouveau interrompue, et la seconde boucle appelle ArchiveAsync(), qui obtient une tâche démarré et commence la boucle 3, répétition ad nauseam .

La clé retourne le contrôle à l'appelant tandis que les releveurs lourds d'exécution.

Le mot-clé await ne présente pas la concurrence. Il est comme le mot-clé yield, il indique au compilateur de restructurer votre code dans lambda contrôlé par une machine d'état.

Pour voir quel code await ressemblerait sans 'await' voir cet excellent lien: http://blogs.msdn.com/b/windowsappdev/archive/2012/04/24/diving-deep-with-winrt-and-await .aspx

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