web-dev-qa-db-fra.com

Comment justifier l'utilisation de l'attente au lieu de .Result () ou .Wait () dans .NET Core?

Depuis la création de .NET Core, les applications de console, les applications de fonction, ASP.NET, etc. n'utilisent pas le contexte de synchronisation dans les méthodes asynchrones (elles se synchronisent donc directement avec Thread Pool).

Cela signifie que l'utilisation de .Result() ou .Wait() après la méthode asynchrone ne provoque plus d'interblocage et affecte uniquement les performances (réservation d'un thread), ce qui n'est généralement pas un problème dans les petits single- des applications métier filetées qui ne lisent et n'écrivent que des données d'un endroit à un autre.

Maintenant, la plupart de nos développeurs utilisent heureusement .Result() et .Wait() car il semble beaucoup plus agréable que le mot clé séparé await. Et la taille des projets est si petite qu'ils sont les seuls développeurs de ces projets.

Comment puis-je justifier l'utilisation du mot clé awaitlorsque les autres développeurs ne voient aucune incitation à l'utiliser? Je peux voir que ce comportement peut entraîner des problèmes dans des projets plus importants.

EDITE la question. Voir les commentaires.

3
kor_

Vous avez manqué le point d'async/attendre

Il n'a pas été créé pour frustrer les développeurs avec des blocages. Il a été créé pour libérer de la puissance de traitement pendant que nous attendons la fin d'une tâche non basée sur le processeur.

L'absence de contexte de synchronisation dans dotnetcore peut vous éviter des blocages inattendus, mais cela signifie également que vous devez vous soucier de la famine des threads.

Chaque fois que vous bloquez un thread avec task.Wait () ou task.Result (), c'est un thread de moins que votre application pourrait utiliser pour faire des choses.

L'utilisation de l'attente libère ce fil à utiliser pour d'autres tâches.

Là où vous auriez eu une impasse auparavant, vous n'avez plus de threads.

https://docs.Microsoft.com/en-us/aspnet/core/performance/performance-best-practices?view=aspnetcore-3.

Je vais risquer une expérience de pensée pour démontrer le point sans vraiment tester le code. Toujours un risque car les choses ne sont jamais aussi simples qu'elles le paraissent.

Disons que nous avons un site de commerce électronique. le client passe des commandes via un appel api au backend. L'API enregistre la commande dans une base de données, mais certaines commandes nécessitent une vérification de la fraude via une API externe qui est parfois très lente, disons 60 secondes.

Pendant le fonctionnement normal, vous recevez quelques commandes par minute et le site fonctionne correctement. Pendant votre vente de Noël, vous obtenez une commande par seconde.

Vous exécutez sur une machine à 4 cœurs, si votre API bloque le contrôle de fraude, vous êtes à 3 threads, si 3 autres commandes de contrôle de fraude arrivent avant que la première ne termine, les commandes commencent à faire la queue. L'API est verrouillée.

Un ordre lent termine et libère un thread, mais vous avez maintenant un arriéré de demandes à rattraper et chacune de ces demandes a un délai d'expiration.

Ainsi, aux heures de pointe, les clients commencent à voir "L'erreur n'a pas pu passer commande: délai d'attente"

Si vous attendez plutôt l'appel de fraude, le thread est immédiatement libéré lorsque l'appel est effectué et peut répondre aux demandes jusqu'à ce que la réponse revienne. Vous n'avez qu'une commande par seconde et 4 threads suffisent.

8
Ewan

Le plus grand facteur, à mon avis, est que l'utilisation des membres Result/Wait fait que la méthode que vous écrivez est synchrone plutôt qu'asynchrone. Autrement dit, vous décidez explicitement d'écrire une fonction synchrone et modifiez la sémantique des méthodes que vous appelez pour s'adapter à ce modèle. Cela signifie que chaque méthode qui pourrait avoir besoin d'appeler votre méthode devra le faire de manière synchrone, plutôt que de pouvoir le faire de manière asynchrone, ce qui pourrait être le cas si vous utilisiez le mot clé await et autorisiez le mode asynchrone comportement des autres fonctions que vous appelez à propager.

Ceci est important car vous ne savez jamais comment une méthode que vous écrivez, en particulier une méthode publique, pourrait être utilisée plus tard par un autre développeur. Il n'est peut-être pas dans un endroit où le filetage et les performances sont une préoccupation maintenant, mais ils peuvent bien être pertinents quelque part qu'un futur consommateur utilise la méthode, et vous ne le saurez pas au moment où vous l'écrivez. Sauf si vous avez absolument besoin que votre méthode soit synchrone pour une raison quelconque, la meilleure option est de permettre à la nature asynchrone des autres méthodes que vous appelez de se propager via votre méthode également, en utilisant await et en faisant votre méthode async.

2
Aaron M. Eshbach

Le "bénéfice évident", comme vous le constatez, est qu'il affecte les performances. Votre question ne précise pas quelles méthodes asynchrones vous utilisez, mais si les performances ne sont pas un problème, vous pouvez simplement utiliser les versions synchrones de ces méthodes (qui sont disponibles dans de nombreuses API) et ne pas traiter du tout avec l'asynchronie. Si cela se produit dans le contexte d'ASP.NET cependant, où il y a une concurrence implicite (gestion de plusieurs demandes), il serait conseillé d'utiliser l'async/wait pour ne pas entraver les performances de traitement des demandes.

0
Yuli Bonner