Mélange efficace des méthodes de synchronisation et async dans une seule méthode?

11

D'accord, cela semble étrange, mais le code est très simple et explique bien la situation.

public virtual async Task RemoveFromRoleAsync(AzureTableUser user, string role)
{
    AssertNotDisposed();
    var roles = await GetRolesForUser(user);
    roles.Roles = RemoveRoles(roles.Roles, role);
    await Run(TableOperation.Replace(roles));
}

(Je sais que je parle en quelque sorte dans l'abstrait ci-dessous, mais ce qui précède est une méthode réelle de ce qui sera le code de production réel qui fait réellement ce que je demande ici, et je suis réellement intéressé par votre examen pour l'exactitude par rapport au modèle asynchrone / attente.)

Je rencontre ce modèle de plus en plus souvent maintenant que j'utilise async/ awaitmore. Le modèle se compose de la chaîne d'événements suivante:

  1. Attendez un premier appel qui me fournira des informations sur lesquelles je dois travailler
  2. Travailler sur ces informations de manière synchrone
  3. Attendre un dernier appel qui enregistre le travail mis à jour

Le bloc de code ci-dessus est généralement la façon dont je gère ces méthodes. Je awaitle premier appel, que je dois parce qu'il est asynchrone. Ensuite, je fais le travail que je dois faire, qui n'est pas lié aux E / S ou aux ressources, et qui n'est donc pas asynchrone. Enfin, j'enregistre mon travail qui est aussi un asyncappel, et hors du cargo-culte je awaitle fais .

Mais est-ce la façon la plus efficace / correcte de gérer ce modèle? Il me semble que je pourrais sauter awaitle dernier appel, mais qu'en est-il en cas d'échec? Et dois-je utiliser une Taskméthode telle que ContinueWithde chaîner mon travail synchrone avec l'appel d'origine? Je suis juste à un point en ce moment où je ne sais pas si je gère cela correctement.

Compte tenu du code de l'exemple , existe-t-il une meilleure façon de gérer cette chaîne d'appel de méthode async / sync / async?

Arnaqué
la source
Le code me semble aussi court et compréhensible que possible. Tout ce à quoi je peux penser introduit une complexité inutile.
Euphoric
@ Euphoric: Dois-je prendre la peine d'attendre mon dernier appel? Que se passerait-il si je ne le faisais pas et que ça le jetait? Serait-ce différent comme c'est le cas actuellement?
Arnaqué le

Réponses:

3

Oui, je pense que c'est la bonne façon de procéder.

Vous ne pouvez pas sauter la seconde await. Si vous le faisiez, la méthode semblerait se terminer trop tôt (avant que la suppression ne soit réellement effectuée) et vous ne sauriez jamais si la suppression a échoué.

Je ne vois pas comment cela pourrait ContinueWith()aider quoi que ce soit ici. Vous pouvez l'utiliser pour éviter de l'utiliser await, mais cela rendrait votre code plus compliqué et moins lisible. Et c'est là tout l'intérêt await: simplifier l'écriture de code asynchrone, par rapport à l'utilisation de continuations.

svick
la source
0

La façon de gérer ce modèle consiste à garantir que toutes les E / S sont asynchrones. Les méthodes d'E / S synchrones provoquent le blocage du thread actuel pendant qu'il attend une réponse de la destination d'E / S (réseau, système de fichiers, etc.).

Une autre chose à considérer est celle qui awaitdoit être utilisée lorsque vous avez besoin d'une valeur de retour ou lorsque vous avez besoin du code attendu pour terminer avant de faire autre chose. Si vous n'avez besoin d'aucune de ces choses, vous pouvez "tirer et oublier" votre méthode asynchrone avec Task.Run.

Ainsi, pour l'utilisation la plus efficace des ressources informatiques, s'il y a RemoveRolesdes E / S, elles doivent devenir await RemoveRolesAsyncet les méthodes d'E / S appelées par RemoveRolesAsyncdoivent également être asynchrones (et éventuellement attendues).

Si les performances ne sont pas votre principale préoccupation, il est possible d'effectuer des E / S synchrones sur un thread asynchrone. C'est une dette technique cependant. (Dans ce cas, vous souhaiterez peut -être appeler la première méthode asynchrone avec ConfigureAwait, selon l'emplacement d'exécution du code.)

Voici un aperçu plus approfondi des meilleures pratiques - https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

Voici quelques notes sur le comportement de ConfigureAwait dans différents environnements comme ASP.NET, WebAPI, etc. - /programming/13489065/best-practice-to-call-configureawait-for-all-server-side -code

Wayne Bloss
la source
2
Vous ne devez jamais tirer et oublier le code avec Task.Run. Toutes les tâches doivent être attendues. Si vous n'attendez pas la tâche et que la tâche est récupérée dans un état où l'exception est "non observée", elle provoquera une exception UnobservedTaskException lors de l'exécution et pourrait bloquer votre application en fonction de la version du framework.
Triynko