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;
}
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:
.ConfigureAwait(false)
zu Ihrer Bibliotheksmethode hinzufügen oder führen Sie Ihre asynchrone Methode explizit in einem Threadpool-Thread aus und warten Sie, bis sie beendet ist:
string code = Task.Run(GenerateCodeAsync).Result;
Sie sollten den Kellner abrufen (GetAwaiter()
) und das Warten auf den Abschluss der asynchronen Task beenden (GetResult()
).
string code = GenerateCodeAsync().GetAwaiter().GetResult();
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" ;
}
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 .
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();
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;
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;