L'appel de méthode asynchrone synchrone

j'ai une méthode async :

public async Task<string> GenerateCodeAsync()
{
    string code = await GenerateCodeService.GenerateCodeAsync();
    return code;
}

je dois appeler cette méthode d'une méthode synchrone.

Comment puis-je faire cela sans avoir à dupliquer la méthode GenerateCodeAsync pour que cela fonctionne de manière synchrone?

mise à Jour

mais aucune solution raisonnable trouvée.

cependant, je vois que HttpClient met déjà en œuvre ce pattern

using (HttpClient client = new HttpClient())
{
    // async
    HttpResponseMessage responseAsync = await client.GetAsync(url);

    // sync
    HttpResponseMessage responseSync = client.GetAsync(url).Result;
}
180
demandé sur Catalin 2014-03-25 11:34:14

6 réponses

vous pouvez accéder à la propriété Result de la tâche, qui bloquera votre thread jusqu'à ce que le résultat soit disponible:

string code = GenerateCodeAsync().Result;

Note: dans certains cas, cela pourrait conduire à une impasse: votre appel à Result bloque le thread principal, empêchant ainsi le reste du code asynchrone d'exécuter. Vous avez les options suivantes pour vous assurer que cela n'arrive pas:

238
répondu Heinzi 2014-03-25 08:47:47

vous devez obtenir l'awaiter ( GetAwaiter() ) et mettre fin à l'attente pour l'achèvement de la tâche asynchrone ( GetResult() ).

string code = GenerateCodeAsync().GetAwaiter().GetResult();
45
répondu Diego Torres 2016-11-28 23:21:51

Vous devriez être en mesure d'obtenir ce fait à l'aide de délégués, expression lambda

private void button2_Click(object sender, EventArgs e)
    {

        label1.Text = "waiting....";

        Task<string> sCode = Task.Run(async () =>
        {
            string msg =await GenerateCodeAsync();
            return msg;
        });

        label1.Text += sCode.Result;

    }

    private Task<string> GenerateCodeAsync()
    {
        return Task.Run<string>(() => GenerateCode());
    }

    private string GenerateCode()
    {
        Thread.Sleep(2000);
        return "I m back" ;
    }
27
répondu Faiyaz 2017-07-03 12:44:25

je dois appeler cette méthode d'une méthode synchrone.

c'est possible avec GenerateCodeAsync().Result ou GenerateCodeAsync().Wait() , comme le suggère l'autre réponse. Cela bloquerait le fil courant jusqu'à ce que GenerateCodeAsync soit terminé.

cependant, votre question est marquée avec , et vous avez aussi laissé le commentaire:

j'espérais un plus simple solution, en pensant que asp.net manipulé ce beaucoup plus facile que d'écrire autant de lignes de code

Mon point est, vous ne devrait pas être bloquant sur une méthode asynchrone dans ASP.NET. Cela permettra de réduire l'évolutivité de votre application web, et peut créer un blocage (quand un await continuation de l'intérieur GenerateCodeAsync est affiché pour AspNetSynchronizationContext ). En utilisant Task.Run(...).Result pour décharger quelque chose à un fil de piscine et puis le bloc évolutivité encore plus, car il engage +1 plus de thread pour traiter une requête HTTP donnée.

ASP.NET a intégré un support pour les méthodes asynchrones, soit par l'intermédiaire de contrôleurs asynchrones (en ASP.NET MVC et API Web) ou directement via AsyncManager et PageAsyncTask en classic ASP.NET. Tu devrais t'en servir. Pour plus de détails, consultez cette réponse .

17
répondu noseratio 2017-05-23 12:34:53

Microsoft Identity a des méthodes d'extension qui appellent les méthodes async de façon synchrone. Par exemple, il y a Générateuseridentityasync () méthode et égal CreateIdentity ()

si vous regardez UserManagerExtensions.CreateIdentity() il ressemble à ceci:

 public static ClaimsIdentity CreateIdentity<TUser, TKey>(this UserManager<TUser, TKey> manager, TUser user,
        string authenticationType)
        where TKey : IEquatable<TKey>
        where TUser : class, IUser<TKey>
    {
        if (manager == null)
        {
            throw new ArgumentNullException("manager");
        }
        return AsyncHelper.RunSync(() => manager.CreateIdentityAsync(user, authenticationType));
    }

voyons maintenant ce Qu'AsyncHelper.RunSync does

  public static TResult RunSync<TResult>(Func<Task<TResult>> func)
    {
        var cultureUi = CultureInfo.CurrentUICulture;
        var culture = CultureInfo.CurrentCulture;
        return _myTaskFactory.StartNew(() =>
        {
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = cultureUi;
            return func();
        }).Unwrap().GetAwaiter().GetResult();
    }

donc, ceci est votre enveloppe pour la méthode async. Et s'il vous plaît ne lisez pas les données du résultat - il sera bloquez potentiellement votre code dans ASP.

il y a un autre moyen - qui est suspect pour moi, mais vous pouvez le considérer aussi

  Result r = null;

            YourAsyncMethod()
                .ContinueWith(t =>
                {
                    r = t.Result;
                })
                .Wait();
13
répondu Vitaliy Markitanov 2017-07-18 22:03:29

pour éviter les blocages j'essaie toujours d'utiliser Task.Run() quand je dois appeler une méthode async synchrone que @Heinzi mentionne.

toutefois, la méthode doit être modifiée si la méthode async utilise des paramètres. Par exemple Task.Run(GenerateCodeAsync("test")).Result donne l'erreur:

Argument 1: ne peut pas convertir de ' System.Threading.Tasks.Task<string> " pour le Système de la.Action 151950920"

cela pourrait être appelé comme ceci à la place:

string code = Task.Run(() => GenerateCodeAsync("test")).Result;
0
répondu Ogglas 2018-09-20 08:05:32