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 aus aufrufen.
Wie kann ich dies tun, ohne die GenerateCodeAsync
Methode duplizieren zu müssen, damit dies synchron funktioniert?
Aktualisieren
Es wurde jedoch keine vernünftige Lösung gefunden.
Ich sehe jedoch, dass HttpClient
dieses Muster bereits implementiert ist
using (HttpClient client = new HttpClient())
{
// async
HttpResponseMessage responseAsync = await client.GetAsync(url);
// sync
HttpResponseMessage responseSync = client.GetAsync(url).Result;
}
Antworten:
Sie können auf die
Result
Eigenschaft der Aufgabe zugreifen , wodurch Ihr Thread blockiert wird, bis das Ergebnis verfügbar ist:Hinweis: In einigen Fällen kann dies zu einem Deadlock führen: Ihr Aufruf zum
Result
Blockieren des Hauptthreads verhindert, dass der Rest des asynchronen Codes ausgeführt wird. Sie haben folgende Möglichkeiten, um sicherzustellen, dass dies nicht geschieht:.ConfigureAwait(false)
Ihrer Bibliotheksmethode oder hinzuFühren Sie Ihre asynchrone Methode explizit in einem Thread-Pool-Thread aus und warten Sie, bis sie abgeschlossen ist:
Dies bedeutet nicht , dass Sie
.ConfigureAwait(false)
nach all Ihren asynchronen Aufrufen nur sinnlos hinzufügen sollten ! Eine detaillierte Analyse, warum und wann Sie verwenden sollten.ConfigureAwait(false)
, finden Sie im folgenden Blog-Beitrag:quelle
result
Gefahr eines Deadlocks birgt, wann ist es dann sicher, das Ergebnis zu erhalten? Benötigt jeder asynchrone AufrufTask.Run
oderConfigureAwait(false)
?AspNetSynchronizationContext.Post
serialisiert async Fortsetzungen:Task newTask = _lastScheduledTask.ContinueWith(_ => SafeWrapCallback(action)); _lastScheduledTask = newTask;
Task.Run
. Oder verwenden Sie so etwas wieWithNoContext
, um redundantes Thread-Switching zu reduzieren..Result
kann immer noch blockieren, wenn sich der Anrufer im Thread-Pool selbst befindet. Stellen Sie sich ein Szenario vor, in dem der Thread-Pool die Größe 32 hat und 32 Aufgaben ausgeführt werden undWait()/Result
auf eine noch zu planende 33. Aufgabe warten, die auf einem der wartenden Threads ausgeführt werden soll.Sie sollten den awaiter (
GetAwaiter()
) erhalten und das Warten auf den Abschluss der asynchronen Task (GetResult()
) beenden .quelle
Task.GetAwaiter
: Diese Methode ist eher für die Verwendung durch den Compiler als für die Verwendung im Anwendungscode vorgesehen.Sie sollten in der Lage sein, dies mit Delegaten, Lambda-Ausdruck, zu erledigen
quelle
Es ist möglich mit
GenerateCodeAsync().Result
oderGenerateCodeAsync().Wait()
, wie die andere Antwort nahelegt. Dies würde den aktuellen Thread blockieren, bis erGenerateCodeAsync
abgeschlossen ist.Ihre Frage ist jedoch mit markiert asp.netund du hast auch den Kommentar hinterlassen:
Mein Punkt ist, dass Sie eine asynchrone Methode in ASP.NET nicht blockieren sollten . Dies verringert die Skalierbarkeit Ihrer Web-App und kann zu einem Deadlock führen (wenn eine
await
Fortsetzung im Innern vonGenerateCodeAsync
veröffentlicht wirdAspNetSynchronizationContext
). Wenn SieTask.Run(...).Result
etwas in einen Pool-Thread auslagern und dann blockieren, wird die Skalierbarkeit noch mehr beeinträchtigt, da +1 mehr Thread für die Verarbeitung einer bestimmten HTTP-Anforderung erforderlich ist.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
undPageAsyncTask
in klassischem ASP.NET. Du solltest es benutzen. Weitere Informationen finden Sie in dieser Antwort .quelle
SaveChanges()
Methode vonDbContext
, und hier rufe ich die asynchronen Methoden auf, so dass mir der asynchrone Controller in dieser Situation leider nicht hilftSaveChangesAsync
undSaveChanges
nur sicherstellen, dass sie nicht beide im selben ASP.NET-Projekt aufgerufen werden..NET MVC
Filter unterstützen asynchronen Code, zum BeispielIAuthorizationFilter
, so dass ich nicht verwenden können , denasync
ganzen WegMicrosoft Identity verfügt über Erweiterungsmethoden, die asynchrone Methoden synchron aufrufen. Zum Beispiel gibt es die GenerateUserIdentityAsync () -Methode und die gleiche CreateIdentity ()
Wenn Sie sich UserManagerExtensions.CreateIdentity () ansehen, sieht es folgendermaßen aus:
Nun wollen wir sehen, was AsyncHelper.RunSync macht
Dies ist also Ihr Wrapper für die asynchrone Methode. Und bitte lesen Sie keine Daten aus dem Ergebnis - es wird möglicherweise Ihren Code in ASP blockieren.
Es gibt einen anderen Weg - der für mich verdächtig ist, aber Sie können ihn auch in Betracht ziehen
quelle
Um Deadlocks zu vermeiden, versuche ich immer, diese zu verwenden,
Task.Run()
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
Task.Run(GenerateCodeAsync("test")).Result
gibt der Fehler:Dies könnte stattdessen so genannt werden:
quelle
Die meisten Antworten in diesem Thread sind entweder komplex oder führen zu einem Deadlock.
Die folgende Methode ist einfach und vermeidet Deadlocks, da wir darauf warten, dass die Aufgabe abgeschlossen ist und erst dann das Ergebnis erhält.
Darüber hinaus finden Sie hier einen Verweis auf einen MSDN-Artikel, der genau dasselbe behandelt: https://blogs.msdn.microsoft.com/jpsanders/2017/08/28/asp-net-do-not-use-task-result- im Hauptkontext /
quelle
Ich bevorzuge einen nicht blockierenden Ansatz:
quelle
Nun, ich benutze diesen Ansatz:
quelle
Der andere Weg könnte sein, wenn Sie warten möchten, bis die Aufgabe abgeschlossen ist:
quelle
BEARBEITEN:
Task verfügt über die Wait-Methode Task.Wait (), die darauf wartet, dass das "Versprechen" aufgelöst wird, und dann fortfährt, wodurch es synchronisiert wird. Beispiel:
quelle
Wenn Sie eine asynchrone Methode namens " RefreshList " haben, können Sie diese asynchrone Methode von einer nicht asynchronen Methode wie unten aufrufen.
quelle