it-swarm.com.de

Asynchrone Methode synchron aufrufen

Ich habe eine async Methode:

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

Ich muss diese Methode von einer synchronen Methode aufrufen.

Wie kann ich dies tun, ohne die Methode GenerateCodeAsync duplizieren zu müssen, damit dies synchron funktioniert?

pdate

Noch keine vernünftige Lösung gefunden.

Ich sehe jedoch, dass HttpClient dieses Muster bereits implementiert

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

    // sync
    HttpResponseMessage responseSync = client.GetAsync(url).Result;
}
213
Catalin

Sie können auf die Eigenschaft Result der Aufgabe zugreifen, wodurch Ihr Thread blockiert wird, bis das Ergebnis verfügbar ist:

string code = GenerateCodeAsync().Result;

Hinweis: In einigen Fällen kann dies zu einem Deadlock führen: Ihr Aufruf von Result blockiert den Haupt-Thread und verhindert so, dass der Rest des asynchronen Codes ausgeführt wird. Sie haben die folgenden Möglichkeiten, um sicherzustellen, dass dies nicht geschieht:

263
Heinzi

Sie sollten den Kellner abrufen (GetAwaiter()) und das Warten auf den Abschluss der asynchronen Task beenden (GetResult()).

string code = GenerateCodeAsync().GetAwaiter().GetResult();
54
Diego Torres

Sie sollten in der Lage sein, dies mit Delegaten, Lambda-Ausdruck, zu erledigen

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" ;
    }
29
Faiyaz

Ich muss diese Methode von einer synchronen Methode aufrufen.

Es ist mit GenerateCodeAsync().Result oder GenerateCodeAsync().Wait() möglich, wie die andere Antwort nahelegt. Dies würde den aktuellen Thread blockieren, bis GenerateCodeAsync abgeschlossen ist.

Ihre Frage ist jedoch mit asp.net markiert, und Sie haben auch den Kommentar hinterlassen:

Ich hatte auf eine einfachere Lösung gehofft und dachte, dass asp.net dies viel einfacher handhabt als das Schreiben von so vielen Codezeilen

Mein Punkt ist, Sie sollten nicht auf einer asynchronen Methode in ASP.NET blockieren . Dies verringert die Skalierbarkeit Ihrer Web-App und kann zu einem Deadlock führen (wenn eine await Fortsetzung in GenerateCodeAsync an AspNetSynchronizationContext gesendet wird). Die Verwendung von Task.Run(...).Result, um etwas in einen Pool-Thread zu verschieben und dann zu blockieren, beeinträchtigt die Skalierbarkeit noch mehr, da +1 mehr Thread für die Verarbeitung einer bestimmten HTTP-Anforderung anfallen.

ASP.NET bietet integrierte Unterstützung für asynchrone Methoden, entweder über asynchrone Controller (in ASP.NET MVC und Web API) oder direkt über AsyncManager und PageAsyncTask in klassischem ASP.NET. Du solltest es benutzen. Weitere Informationen erhalten Sie unter diese Antwort .

20
noseratio

Microsoft Identity verfügt über Erweiterungsmethoden, die asynchrone Methoden synchron aufrufen. Zum Beispiel gibt es die GenerateUserIdentityAsync () -Methode und die gleiche CreateIdentity () -Methode.

Wenn Sie sich UserManagerExtensions.CreateIdentity () ansehen, sieht es folgendermaßen aus:

 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));
    }

Nun wollen wir sehen, was AsyncHelper.RunSync macht

  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();
    }

Dies ist also Ihr Wrapper für die asynchrone Methode. Und bitte lesen Sie keine Daten aus Result - es wird möglicherweise Ihren Code in ASP blockieren.

Es gibt einen anderen Weg - der für mich misstrauisch ist, aber Sie können ihn auch in Betracht ziehen

  Result r = null;

            YourAsyncMethod()
                .ContinueWith(t =>
                {
                    r = t.Result;
                })
                .Wait();
17

Um Deadlocks zu vermeiden, versuche ich immer, Task.Run() zu verwenden, wenn ich eine von @Heinzi erwähnte asynchrone Methode synchron aufrufen muss.

Die Methode muss jedoch geändert werden, wenn die asynchrone Methode Parameter verwendet. Zum Beispiel gibt Task.Run(GenerateCodeAsync("test")).Result den Fehler aus:

Argument 1: Konvertierung von 'System.Threading.Tasks.Task<string>' nach 'System.Action' nicht möglich

Dies könnte stattdessen so heißen:

string code = Task.Run(() => GenerateCodeAsync("test")).Result;
5
Ogglas

Der andere Weg könnte sein, wenn Sie warten möchten, bis die Aufgabe abgeschlossen ist:

var t = GenerateCodeService.GenerateCodeAsync();
Task.WhenAll(t);
string code = t.Result;
0
frablaser