J'ai une async
méthode:
public async Task<string> GenerateCodeAsync()
{
string code = await GenerateCodeService.GenerateCodeAsync();
return code;
}
J'ai besoin d'appeler cette méthode à partir d'une méthode synchrone.
Comment puis-je faire cela sans avoir à dupliquer la GenerateCodeAsync
méthode pour que cela fonctionne de manière synchrone?
Mettre à jour
Pourtant, aucune solution raisonnable n'a été trouvée.
Cependant, je vois que HttpClient
déjà met en œuvre ce modèle
using (HttpClient client = new HttpClient())
{
// async
HttpResponseMessage responseAsync = await client.GetAsync(url);
// sync
HttpResponseMessage responseSync = client.GetAsync(url).Result;
}
Réponses:
Vous pouvez accéder à la
Result
propriété de la tâche, ce qui entraînera le blocage de votre thread jusqu'à ce que le résultat soit disponible:Remarque: Dans certains cas, cela peut entraîner un blocage: votre appel à
Result
bloque le thread principal, empêchant ainsi l'exécution du reste du code asynchrone. Vous disposez des options suivantes pour vous assurer que cela ne se produit pas:.ConfigureAwait(false)
à votre méthode de bibliothèque ouexécutez explicitement votre méthode async dans un thread de pool de threads et attendez qu'elle se termine:
Cela ne signifie pas que vous devez simplement ajouter sans réfléchir
.ConfigureAwait(false)
après tous vos appels asynchrones! Pour une analyse détaillée sur pourquoi et quand vous devez utiliser.ConfigureAwait(false)
, consultez l'article de blog suivant:la source
result
risque un blocage, alors quand est- il sûr d'obtenir le résultat? Est-ce que chaque appel asynchrone nécessiteTask.Run
ouConfigureAwait(false)
?AspNetSynchronizationContext.Post
sérialisation des continuations asynchrones:Task newTask = _lastScheduledTask.ContinueWith(_ => SafeWrapCallback(action)); _lastScheduledTask = newTask;
Task.Run
pour rester en sécurité. Ou utilisez quelque chose commeWithNoContext
pour réduire le changement de thread redondant..Result
peut toujours se bloquer si l'appelant se trouve sur le pool de threads lui-même. Prenez un scénario dans lequel le pool de threads est de taille 32 et 32 tâches sont en cours d'exécution etWait()/Result
attendent une 33e tâche encore à planifier qui veut s'exécuter sur l'un des threads en attente.Vous devez obtenir le waiter (
GetAwaiter()
) et mettre fin à l'attente de la fin de la tâche asynchrone (GetResult()
).la source
Task.GetAwaiter
: cette méthode est destinée à être utilisée par le compilateur plutôt qu'à être utilisée dans le code d'application.Vous devriez pouvoir faire cela en utilisant des délégués, expression lambda
la source
C'est possible avec
GenerateCodeAsync().Result
ouGenerateCodeAsync().Wait()
, comme le suggère l'autre réponse. Cela bloquerait le thread actuel jusqu'à ce qu'ilGenerateCodeAsync
soit terminé.Cependant, votre question est étiquetée avec asp.net, et vous avez également laissé le commentaire:
Mon point est, vous ne devriez pas bloquer sur une méthode asynchrone dans ASP.NET. Cela réduira l'évolutivité de votre application Web et peut créer un blocage (lorsqu'une
await
suite à l'intérieurGenerateCodeAsync
est publiéeAspNetSynchronizationContext
). UtiliserTask.Run(...).Result
pour décharger quelque chose sur un thread de pool puis bloquer bloquera encore plus l'évolutivité, car cela engendre +1 thread supplémentaire pour traiter une requête HTTP donnée.ASP.NET prend en charge les méthodes asynchrones, soit via des contrôleurs asynchrones (dans ASP.NET MVC et API Web), soit directement via
AsyncManager
etPageAsyncTask
dans ASP.NET classique. Vous devez l'utiliser. Pour plus de détails, consultez cette réponse .la source
SaveChanges()
méthode deDbContext
, et ici j'appelle les méthodes asynchrones, donc malheureusement le contrôleur asynchrone ne m'aidera pas dans cette situationSaveChangesAsync
etSaveChanges
assurez-vous simplement qu'ils ne sont pas appelés les deux dans le même projet ASP.NET..NET MVC
exempleIAuthorizationFilter
, tous les filtres ne prennent pas en charge le code asynchrone , donc je ne peux pas utiliserasync
tout le cheminMicrosoft Identity possède des méthodes d'extension qui appellent les méthodes asynchrones de manière synchrone. Par exemple, il existe une méthode GenerateUserIdentityAsync () et un égal CreateIdentity ()
Si vous regardez UserManagerExtensions.CreateIdentity (), cela ressemble à ceci:
Voyons maintenant ce que fait AsyncHelper.RunSync
C'est donc votre wrapper pour la méthode asynchrone. Et s'il vous plaît, ne lisez pas les données de Result - cela bloquera potentiellement votre code dans ASP.
Il y a une autre façon - ce qui est suspect pour moi, mais vous pouvez aussi l'envisager
la source
Pour éviter les blocages, j'essaie toujours d'utiliser
Task.Run()
lorsque je dois appeler une méthode asynchrone de manière synchrone mentionnée par @Heinzi.Cependant, la méthode doit être modifiée si la méthode asynchrone utilise des paramètres. Par exemple,
Task.Run(GenerateCodeAsync("test")).Result
donne l'erreur:Cela pourrait être appelé comme ceci à la place:
la source
La plupart des réponses sur ce sujet sont soit complexes, soit entraîner un blocage.
La méthode suivante est simple et elle évitera un blocage car nous attendons que la tâche se termine et que nous obtenions ensuite son résultat.
En outre, voici une référence à un article MSDN qui parle exactement de la même chose - https://blogs.msdn.microsoft.com/jpsanders/2017/08/28/asp-net-do-not-use-task-result- dans-contexte-principal /
la source
Je préfère une approche non bloquante:
la source
Eh bien, j'utilise cette approche:
la source
L'autre façon pourrait être si vous voulez attendre la fin de la tâche:
la source
ÉDITER:
La tâche a la méthode Wait, Task.Wait (), qui attend que la "promesse" soit résolue puis continue, la rendant ainsi synchrone. exemple:
la source
Si vous avez une méthode asynchrone appelée " RefreshList ", vous pouvez appeler cette méthode asynchrone à partir d'une méthode non asynchrone comme ci-dessous.
la source