Envelopper le code synchrone en appel asynchrone

J’ai une méthode dans l’application ASP.NET, qui consum pas mal de temps. Un appel à cette méthode peut se produire jusqu’à 3 fois au cours d’une requête utilisateur, en fonction de l’état du cache et des parameters fournis par l’utilisateur. Chaque appel prend environ 1 à 2 secondes. La méthode en elle-même est un appel synchrone au service et il est impossible de remplacer l’implémentation.
L’appel synchrone au service ressemble donc à ceci:

public OutputModel Calculate(InputModel input) { // do some stuff return Service.LongRunningCall(input); } 

Et l’utilisation de la méthode est (note, cet appel de méthode peut se produire plusieurs fois):

 private void MakeRequest() { // a lot of other stuff: preparing requests, sending/processing other requests, etc. var myOutput = Calculate(myInput); // stuff again } 

J’ai essayé de changer l’implémentation de mon côté pour fournir un travail simultané de cette méthode, et voici ce à quoi je suis arrivé jusqu’ici.

 public async Task CalculateAsync(InputModel input) { return await Task.Run(() => { return Calculate(input); }); } 

Utilisation (une partie du code “do other stuff” s’exécute en même temps que l’appel au service):

 private async Task MakeRequest() { // do some stuff var task = CalculateAsync(myInput); // do other stuff var myOutput = await task; // some more stuff } 

Ma question est la suivante. Est-ce que j’utilise la bonne approche pour accélérer l’exécution dans une application ASP.NET ou est-ce que j’essaie inutilement d’exécuter le code synchrone de manière asynchrone? Quelqu’un peut-il expliquer pourquoi la deuxième approche n’est pas une option dans ASP.NET (si ce n’est vraiment pas)? De même, si une telle approche est applicable, dois-je appeler cette méthode de manière asynchrone si c’est le seul appel que nous pouvons effectuer pour le moment (j’ai un tel cas, quand il n’y a pas d’autre chose à faire en attendant l’achèvement)?
La plupart des articles sur le net sur ce sujet async-await utilisation de l’approche async-await avec le code, qui fournit déjà des méthodes awaitable , mais ce n’est pas mon cas. Voici le bel article décrivant mon cas, qui ne décrit pas la situation des appels parallèles, déclinant l’option d’emballer l’appel de synchronisation, mais à mon avis, ma situation est exactement l’occasion de le faire.
Merci d’avance pour l’aide et les conseils.

Il est important de distinguer deux types de concurrence. La simultanéité asynchrone se produit lorsque vous avez plusieurs opérations asynchrones en vol (et que chaque opération est asynchrone, aucune d’entre elles n’utilise réellement un thread ). La concurrence parallèle est lorsque vous avez plusieurs threads chacun effectuant une opération distincte.

La première chose à faire est de réévaluer cette hypothèse:

La méthode en elle-même est un appel synchrone au service et il est impossible de remplacer l’implémentation.

Si votre “service” est un service Web ou tout autre élément lié aux E / S, la meilleure solution consiste à écrire une API asynchrone pour ce service.

Je suppose que votre “service” est une opération liée au processeur qui doit s’exécuter sur le même ordinateur que le serveur Web.

Si tel est le cas, la prochaine chose à évaluer est une autre hypothèse:

J’ai besoin de la requête pour exécuter plus rapidement.

Êtes-vous absolument sûr que c’est ce que vous devez faire? Existe-t-il des modifications frontales à effectuer – par exemple, lancez la demande et autorisez l’utilisateur à effectuer d’autres tâches pendant le traitement?

Je suppose que oui, il faut vraiment que l’exécution de la demande individuelle soit plus rapide.

Dans ce cas, vous devrez exécuter du code parallèle sur votre serveur Web. Cela n’est certainement pas recommandé en général, car le code parallèle utilisera des threads dont ASP.NET aura peut-être besoin pour gérer d’autres requêtes et en supprimant / ajoutant des threads, il désactivera les heuristiques de pool de threads ASP.NET. Cette décision a donc un impact sur l’ensemble de votre serveur.

Lorsque vous utilisez du code parallèle sur ASP.NET, vous décidez vraiment de limiter l’évolutivité de votre application Web. Vous pouvez également voir une bonne quantité de perte de fil, surtout si vos demandes sont trop volumineuses. Je recommande d’utiliser uniquement le code parallèle sur ASP.NET si vous savez que le nombre d’utilisateurs simultanés sera assez faible (pas un serveur public).

Donc, si vous arrivez aussi loin et que vous êtes sûr de vouloir effectuer un parallel processing sur ASP.NET, vous avez deux options.

Une des méthodes les plus faciles consiste à utiliser Task.Run , très similaire à votre code existant. Cependant, je ne recommande pas d’implémenter une méthode CalculateAsync car cela implique que le traitement est asynchrone (ce qui n’est pas le cas). Au lieu de cela, utilisez Task.Run au moment de l’appel:

 private async Task MakeRequest() { // do some stuff var task = Task.Run(() => Calculate(myInput)); // do other stuff var myOutput = await task; // some more stuff } 

Sinon, si cela fonctionne bien avec votre code, vous pouvez utiliser le type Parallel , à savoir Parallel.For , Parallel.ForEach ou Parallel.Invoke . L’avantage du code Parallel est que le thread de demande est utilisé comme l’un des threads parallèles, puis reprend son exécution dans le contexte du thread (il y a moins de changement de contexte que l’exemple async ):

 private void MakeRequest() { Parallel.Invoke(() => Calculate(myInput1), () => Calculate(myInput2), () => Calculate(myInput3)); } 

Je ne recommande pas d’utiliser Parallel LINQ (PLINQ) sur ASP.NET.