Ich bin auf einige Best Practices für die asynchrone Programmierung mit c # s async
/ await
keywords gestoßen (ich bin neu in c # 5.0).
Einer der gegebenen Ratschläge war der folgende:
Stabilität: Kennen Sie Ihre Synchronisationskontexte
... Einige Synchronisationskontexte sind nicht wiedereintrittsfähig und Single-Threaded. Dies bedeutet, dass jeweils nur eine Arbeitseinheit im Kontext ausgeführt werden kann. Ein Beispiel hierfür ist der Windows-UI-Thread oder der ASP.NET-Anforderungskontext. In diesen Single-Threaded-Synchronisationskontexten ist es einfach, sich selbst zu blockieren. Wenn Sie eine Aufgabe aus einem Single-Thread-Kontext heraus erzeugen und dann im Kontext auf diese Aufgabe warten, blockiert Ihr Wartecode möglicherweise die Hintergrundaufgabe.
public ActionResult ActionAsync()
{
// DEADLOCK: this blocks on the async task
var data = GetDataAsync().Result;
return View(data);
}
private async Task<string> GetDataAsync()
{
// a very simple async method
var result = await MyWebService.GetDataAsync();
return result.ToString();
}
Wenn ich versuche, es selbst zu zerlegen, wird der Haupt-Thread zu einem neuen in MyWebService.GetDataAsync();
, aber da der Haupt-Thread dort wartet, wartet er auf das Ergebnis in GetDataAsync().Result
. In der Zwischenzeit sagen wir, die Daten sind fertig. Warum setzt der Hauptthread seine Fortsetzungslogik nicht fort und gibt ein Zeichenfolgenergebnis von zurück GetDataAsync()
?
Kann mir bitte jemand erklären, warum es im obigen Beispiel einen Deadlock gibt? Ich habe keine Ahnung, wo das Problem liegt ...
quelle
var data = GetDataAsync().Result;
ist eine Codezeile, die niemals in einem Kontext ausgeführt werden sollte, den Sie nicht blockieren sollten (UI- oder ASP.NET-Anforderung). Selbst wenn es nicht blockiert, blockiert es den Thread auf unbestimmte Zeit. Im Grunde ist es ein schreckliches Beispiel. [Sie müssen den UI-Thread verlassen, bevor Sie solchen Code ausführen, oderawait
dort auch verwenden, wie Toni vorschlägt.]Antworten:
Schauen Sie sich dieses Beispiel an , Stephen hat eine klare Antwort für Sie:
Ein weiterer Link, den Sie lesen sollten: Warten und Benutzeroberfläche und Deadlocks! Oh mein!
quelle
GetDataAsync().Result;
Wird ausgeführt, wenn die von zurückgegebene AufgabeGetDataAsync()
abgeschlossen ist. In der Zwischenzeit wird der UI-Thread blockiertreturn result.ToString()
) wird zur Ausführung in den UI-Thread gestelltGetDataAsync()
wird abgeschlossen, wenn die Fortsetzung in der Warteschlange ausgeführt wirdSackgasse!
Der Deadlock kann durch bereitgestellte Alternativen aufgehoben werden, um Fakt 1 oder Fakt 2 zu vermeiden.
var data = await GetDataAsync()
, damit der UI-Thread weiter ausgeführt werden kannvar data = Task.Run(GetDataAsync).Result
, wodurch die Fortsetzung in den Synchronisierungskontext eines Threadpool-Threads gestellt wird. Dadurch kann die von zurückgegebene AufgabeGetDataAsync()
abgeschlossen werden.Dies wird in einem Artikel von Stephen Toub sehr gut erklärt , ungefähr auf halber Strecke, wo er das Beispiel von verwendet
DelayAsync()
.quelle
var data = Task.Run(GetDataAsync).Result
das ist, neu für mich. Ich habe immer gedacht, dass das Äußere.Result
sofort verfügbar sein wird, sobald das erste Warten aufGetDataAsync
getroffen wird, alsodata
wird es immer so seindefault
. Interessant.Ich habe gerade in einem ASP.NET MVC-Projekt wieder an diesem Problem herumgespielt. Wenn Sie
async
Methoden von a aufrufen möchtenPartialView
, dürfen Sie die nicht erstellenPartialView
async
. Sie erhalten eine Ausnahme, wenn Sie dies tun.Sie können die folgende einfache Problemumgehung in dem Szenario verwenden, in dem Sie eine
async
Methode aus einer Synchronisierungsmethode aufrufen möchten :SynchronizationContext
SynchronizationContext
Beispiel:
quelle
Ein weiterer wichtiger Punkt ist, dass Sie Aufgaben nicht blockieren und asynchron verwenden sollten, um Deadlocks zu vermeiden. Dann wird alles asynchron und nicht synchron blockiert.
quelle
Eine Lösung, zu der ich gekommen bin, besteht darin, eine
Join
Erweiterungsmethode für die Aufgabe zu verwenden, bevor ich nach dem Ergebnis frage.Der Code sieht folgendermaßen aus:
Wo die Join-Methode ist:
Ich bin nicht genug in der Domäne, um die Nachteile dieser Lösung zu sehen (falls vorhanden)
quelle