Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
von Rick Anderson
In diesem Lernprogramm lernen Sie die Grundlagen des Erstellens einer asynchronen ASP.NET MVC-Webanwendung mit Visual Studio Express 2012 für Web, einer kostenlosen Version von Microsoft Visual Studio. Sie können auch Visual Studio 2012 verwenden.
Ein vollständiges Beispiel wird für dieses Lernprogramm auf github bereitgestellt. https://github.com/RickAndMSFT/Async-ASP.NET/
Mit der ASP.NET MVC 4 Controller-Klasse in Kombination von .NET 4.5 können Sie asynchrone Aktionsmethoden schreiben, die ein Objekt vom Typ "Task ActionResult<>" zurückgeben. .NET Framework 4 hat ein asynchrones Programmierkonzept eingeführt, das als "Aufgabe " bezeichnet wird und ASP.NET MVC 4 " Task" unterstützt. Aufgaben werden durch den Aufgabentyp und verwandte Typen im Namespace "System.Threading.Tasks " dargestellt. .NET Framework 4.5 baut auf dieser asynchronen Unterstützung mit den await - und asynchronen Schlüsselwörtern auf, die das Arbeiten mit Task-Objekten wesentlich weniger komplex machen als vorherige asynchrone Ansätze. Das Await-Schlüsselwort ist syntaktische Kurzform, um anzugeben, dass ein Codeabschnitt asynchron auf einen anderen Codeabschnitt warten soll. Das asynchrone Schlüsselwort stellt einen Hinweis dar, mit dem Sie Methoden als aufgabenbasierte asynchrone Methoden markieren können. Die Kombination aus Await, async und dem Task-Objekt erleichtert es Ihnen, asynchronen Code in .NET 4.5 zu schreiben. Das neue Modell für asynchrone Methoden wird als aufgabenbasiertes asynchrones Muster (TAP) bezeichnet. In diesem Lernprogramm wird davon ausgegangen, dass Sie mit der asynchronen Programmierung mit await - und asynchronen Schlüsselwörtern und dem Task-Namespace vertraut sind.
Weitere Informationen zur Verwendung von Await - und asynchronen Schlüsselwörtern und dem Task-Namespace finden Sie in den folgenden Verweisen.
- Whitepaper: Asynchronie in .NET
- Häufig gestellte Fragen zu Async/Await
- Asynchrone Programmierung in Visual Studio
Wie Anforderungen vom Threadpool verarbeitet werden
Auf dem Webserver verwaltet das .NET Framework einen Pool von Threads, die für die Bearbeitung von ASP.NET-Anforderungen verwendet werden. Wenn eine Anforderung eingeht, wird ein Thread aus dem Pool zugewiesen, um diese Anforderung zu verarbeiten. Wenn die Anforderung synchron verarbeitet wird, ist der Thread, der die Anforderung verarbeitet, ausgelastet, während die Anforderung verarbeitet wird, und dieser Thread kann keine andere Anforderung verarbeiten.
Dies ist möglicherweise kein Problem, da der Thread-Pool groß genug ist, um viele beschäftigte Threads aufzunehmen. Die Anzahl der Threads im Threadpool ist jedoch begrenzt (der Standardwert für .NET 4.5 ist 5.000). In großen Anwendungen mit hoher Gleichzeitigkeit lang laufender Anforderungen sind möglicherweise alle verfügbaren Threads ausgelastet. Diese Bedingung wird als Threadhunger bezeichnet. Wenn diese Bedingung erreicht ist, werden anforderungen vom Webserver in die Warteschlange gestellt. Wenn die Anforderungswarteschlange voll ist, lehnt der Webserver Anforderungen mit einem HTTP 503-Status ab (Server zu ausgelastet). Der CLR-Threadpool hat Einschränkungen für neue Threadeinfügungen. Wenn die Parallelität platzt (d. h. Ihre Website kann plötzlich eine große Anzahl von Anforderungen abrufen) und alle verfügbaren Anforderungsthreads aufgrund von Back-End-Aufrufen mit hoher Latenz ausgelastet sind, kann die eingeschränkte Threadeinfügungsrate dazu führen, dass Ihre Anwendung sehr schlecht reagiert. Darüber hinaus hat jeder neue Thread, der dem Threadpool hinzugefügt wird, Mehraufwand (z. B. 1 MB Stapelspeicher). Eine Webanwendung, die synchrone Methoden verwendet, um Aufrufe mit hoher Latenz zu verarbeiten, bei denen der Threadpool auf das .NET 4.5-Standard-Maximum von 5. 000 Threads wächst, verbraucht etwa 5 GB mehr Arbeitsspeicher als eine Anwendung, die den Dienst mit asynchronen Methoden und nur 50 Threads unterstützt. Wenn Sie asynchron arbeiten, verwenden Sie nicht immer einen Thread. Wenn Sie beispielsweise eine asynchrone Webdienstanforderung vornehmen, verwendet ASP.NET keine Threads zwischen dem asynchronen Methodenaufruf und dem Await. Die Verwendung des Threadpools für Dienstanforderungen mit hoher Latenz kann zu einem großen Speicherbedarf und einer schlechten Auslastung der Serverhardware führen.
Verarbeiten asynchroner Anforderungen
In einer Web-App, die eine große Anzahl gleichzeitiger Anforderungen beim Start sieht oder eine platzende Last aufweist (wobei die Parallelität plötzlich zunimmt), erhöht das asynchrone Ausführen von Webdienstaufrufen die Reaktionsfähigkeit der App. Eine asynchrone Anforderung benötigt dieselbe Zeit wie eine synchrone Anforderung. Wenn eine Anforderung einen Webdienstaufruf ausführt, der zwei Sekunden erfordert, dauert die Anforderung zwei Sekunden, unabhängig davon, ob sie synchron oder asynchron ausgeführt wird. Während eines asynchronen Aufrufs wird ein Thread jedoch nicht daran gehindert, auf andere Anforderungen zu reagieren, während er auf den Abschluss der ersten Anforderung wartet. Daher verhindern asynchrone Anforderungen die Anhäufung von Anfragen in der Warteschlange und das Wachstum des Threadpools, wenn viele gleichzeitige Anforderungen vorhanden sind, die lang andauernde Vorgänge aufrufen.
Auswählen synchroner oder asynchroner Aktionsmethoden
In diesem Abschnitt werden Richtlinien für die Verwendung synchroner oder asynchroner Aktionsmethoden aufgeführt. Dies sind nur Richtlinien; Überprüfen Sie jede Anwendung einzeln, um festzustellen, ob asynchrone Methoden bei der Leistung helfen.
Verwenden Sie im Allgemeinen synchrone Methoden für die folgenden Bedingungen:
- Die Operationen sind einfach oder kurzzeitig.
- Einfachheit ist wichtiger als Effizienz.
- Die Vorgänge sind in erster Linie CPU-Vorgänge anstelle von Vorgängen, die einen umfangreichen Datenträger- oder Netzwerkaufwand erfordern. Die Verwendung asynchroner Aktionsmethoden für CPU-gebundene Vorgänge bietet keine Vorteile und führt zu mehr Aufwand.
Verwenden Sie im Allgemeinen asynchrone Methoden für die folgenden Bedingungen:
- Sie rufen Dienste auf, die über asynchrone Methoden genutzt werden können, und Sie verwenden .NET 4.5 oder höher.
- Die Vorgänge sind netzwerkgebunden oder I/O-gebunden, anstatt CPU-gebunden.
- Parallelität ist wichtiger als die Einfachheit des Codes.
- Sie möchten einen Mechanismus bereitstellen, mit dem Benutzer eine lange ausgeführte Anforderung abbrechen können.
- Wenn der Vorteil des Wechselns von Threads die Kosten des Kontextwechsels überwiegt. Im Allgemeinen sollten Sie eine Methode asynchron machen, wenn die synchrone Methode auf den ASP.NET Anforderungsthread wartet, während keine Arbeit ausgeführt wird. Durch den asynchronen Aufruf bleibt der ASP.NET-Anforderungsthread nicht untätig, während er auf den Abschluss der Webdienstanforderung wartet.
- Tests zeigen, dass die Blockierungsvorgänge ein Engpass bei der Websiteleistung sind und IIS weitere Anforderungen mithilfe asynchroner Methoden für diese blockierenden Aufrufe verarbeiten kann.
Das herunterladbare Beispiel zeigt, wie asynchrone Aktionsmethoden effektiv verwendet werden. Das bereitgestellte Beispiel wurde entwickelt, um eine einfache Demonstration der asynchronen Programmierung in ASP.NET MVC 4 mit .NET 4.5 bereitzustellen. Das Beispiel soll keine Referenzarchitektur für die asynchrone Programmierung in ASP.NET MVC sein. Das Beispielprogramm ruft ASP.NET Web-API-Methoden auf, die wiederum Task.Delay aufrufen, um lange ausgeführte Webdienstaufrufe zu simulieren. Die meisten Produktionsanwendungen zeigen keine so offensichtlichen Vorteile für die Verwendung asynchroner Aktionsmethoden.
Für wenige Anwendungen müssen alle Aktionsmethoden asynchron sein. Häufig bietet das Konvertieren einiger synchroner Aktionsmethoden in asynchrone Methoden die beste Effizienzsteigerung für die erforderliche Arbeitsmenge.
Die Beispielanwendung
Sie können die Beispielanwendung von https://github.com/RickAndMSFT/Async-ASP.NET/ der GitHub-Website herunterladen. Das Repository besteht aus drei Projekten:
- Mvc4Async: Das ASP.NET MVC 4-Projekt, das den in diesem Lernprogramm verwendeten Code enthält. Es führt Web-API-Aufrufe an den WebAPIpgw-Dienst durch.
-
WebAPIpgw: Das ASP.NET MVC 4-Web-API-Projekt, das die
Products, Gizmos and WidgetsController implementiert. Es stellt die Daten für das WebAppAsync-Projekt und das Mvc4Async-Projekt bereit. - WebAppAsync: Das in einem anderen Lernprogramm verwendete ASP.NET Web Forms-Projekt.
Die Gizmos Synchronaktionsmethode
Der folgende Code zeigt die Gizmos synchrone Aktionsmethode, die zum Anzeigen einer Liste von Gizmos verwendet wird. (Für diesen Artikel ist ein Gizmo ein fiktives mechanisches Gerät.)
public ActionResult Gizmos()
{
ViewBag.SyncOrAsync = "Synchronous";
var gizmoService = new GizmoService();
return View("Gizmos", gizmoService.GetGizmos());
}
Der folgende Code zeigt die GetGizmos Methode des Gizmo-Diensts.
public class GizmoService
{
public async Task<List<Gizmo>> GetGizmosAsync(
// Implementation removed.
public List<Gizmo> GetGizmos()
{
var uri = Util.getServiceUri("Gizmos");
using (WebClient webClient = new WebClient())
{
return JsonConvert.DeserializeObject<List<Gizmo>>(
webClient.DownloadString(uri)
);
}
}
}
Die GizmoService GetGizmos Methode übergibt einen URI an einen ASP.NET Web-API-HTTP-Dienst, der eine Liste von Gizmos-Daten zurückgibt. Das WebAPIpgw-Projekt enthält die Implementierung der Web-API gizmos, widget und product -Controller.
Die folgende Abbildung zeigt die Gizmos-Ansicht aus dem Beispielprojekt.
Erstellen einer asynchronen Gizmos-Aktionsmethode
Im Beispiel werden die neuen Schlüsselwörter async und await (verfügbar in .NET 4.5 und Visual Studio 2012) verwendet, damit der Compiler dafür verantwortlich ist, die komplizierten Transformationen durchzuführen, die für die asynchrone Programmierung erforderlich sind. Mit dem Compiler können Sie Code mit den synchronen Steuerungsflusskonstrukten von C# schreiben, und der Compiler wendet automatisch die Transformationen an, die erforderlich sind, um Rückrufe zu verwenden, um das Blockieren von Threads zu vermeiden.
Der folgende Code zeigt die Gizmos synchrone Methode und die GizmosAsync asynchrone Methode. Wenn Ihr Browser das HTML 5-Element<mark> unterstützt, werden die Änderungen in GizmosAsync in gelber Hervorhebung angezeigt.
public ActionResult Gizmos()
{
ViewBag.SyncOrAsync = "Synchronous";
var gizmoService = new GizmoService();
return View("Gizmos", gizmoService.GetGizmos());
}
public async Task<ActionResult> GizmosAsync()
{
ViewBag.SyncOrAsync = "Asynchronous";
var gizmoService = new GizmoService();
return View("Gizmos", await gizmoService.GetGizmosAsync());
}
Die folgenden Änderungen wurden angewendet, um das GizmosAsync asynchrone Verfahren zuzulassen.
- Die Methode ist mit dem async-Schlüsselwort gekennzeichnet, das dem Compiler angibt, Callbacks für Teile des Methodenrumpfs zu generieren und automatisch ein zurückgegebenes
Task<ActionResult>zu erstellen. - "Async" wurde an den Methodennamen angefügt. Das Anfügen von "Async" ist nicht erforderlich, ist aber die Konvention beim Schreiben asynchroner Methoden.
- Der Rückgabetyp wurde von
ActionResultzuTask<ActionResult>. Der Rückgabetyp vonTask<ActionResult>steht für laufende Arbeiten und bietet den Aufrufern der Methode ein Handle, über das auf den Abschluss des asynchronen Vorgangs gewartet werden kann. In diesem Fall ist der Aufrufer der Webdienst.Task<ActionResult>stellt die laufende Arbeit mit dem Ergebnis vonActionResult.dar - Das Await-Schlüsselwort wurde auf den Webdienstaufruf angewendet.
- Die asynchrone Webdienst-API wurde aufgerufen (
GetGizmosAsync).
Innerhalb des GetGizmosAsync Methodentexts wird eine andere asynchrone Methode GetGizmosAsync aufgerufen.
GetGizmosAsync gibt sofort ein Task<List<Gizmo>> zurück, das schließlich abgeschlossen sein wird, sobald die Daten verfügbar sind. Da Sie nichts anderes tun möchten, bis Sie die Gizmo-Daten haben, wartet der Code auf die Aufgabe (unter Verwendung des Await-Schlüsselworts). Sie können das Await-Schlüsselwort nur in Methoden verwenden, die mit dem asynchronen Schlüsselwort versehen sind.
Das Await-Schlüsselwort blockiert den Thread nicht, bis die Aufgabe abgeschlossen ist. Er registriert den Rest der Methode als Rückruf für die Aufgabe und gibt sofort zurück. Wenn die erwartete Aufgabe schließlich abgeschlossen ist, wird dieser Rückruf aufgerufen und somit die Ausführung der Methode direkt an der Stelle fortgesetzt, an der sie unterbrochen wurde. Weitere Informationen zur Verwendung der await - und asynchronen Schlüsselwörter und des Task-Namespace finden Sie in den asynchronen Verweisen.
Der folgende Code zeigt die GetGizmos und GetGizmosAsync Methoden.
public List<Gizmo> GetGizmos()
{
var uri = Util.getServiceUri("Gizmos");
using (WebClient webClient = new WebClient())
{
return JsonConvert.DeserializeObject<List<Gizmo>>(
webClient.DownloadString(uri)
);
}
}
public async Task<List<Gizmo>> GetGizmosAsync()
{
var uri = Util.getServiceUri("Gizmos");
using (HttpClient httpClient = new HttpClient())
{
var response = await httpClient.GetAsync(uri);
return (await response.Content.ReadAsAsync<List<Gizmo>>());
}
}
Die asynchronen Änderungen ähneln denen, die oben an der GizmosAsync vorgenommen wurden.
- Die Methodensignatur wurde mit dem async Schlüsselwort annotiert, der Rückgabetyp wurde geändert
Task<List<Gizmo>>, und Async wurde an den Methodennamen angefügt. - Die asynchrone HttpClient-Klasse wird anstelle der WebClient-Klasse verwendet.
- Das Await-Schlüsselwort wurde auf die asynchronen HttpClient-Methoden angewendet.
Die folgende Abbildung zeigt die asynchrone Gizmo-Ansicht.
Die Browserpräsentation der Gizmos-Daten ist identisch mit der Ansicht, die vom synchronen Aufruf erstellt wurde. Der einzige Unterschied besteht darin, dass die asynchrone Version bei schweren Lasten leistungsintensiver sein kann.
Ausführen mehrerer Vorgänge parallel
Asynchrone Aktionsmethoden haben einen erheblichen Vorteil gegenüber synchronen Methoden, wenn eine Aktion mehrere unabhängige Vorgänge ausführen muss. In der bereitgestellten Stichprobe zeigt die synchrone Methode PWG(für Produkte, Widgets und Gizmos) die Ergebnisse von drei Webdienstaufrufen an, um eine Liste von Produkten, Widgets und Gizmos zu erhalten. Das ASP.NET Web-API-Projekt , das diese Dienste bereitstellt, verwendet Task.Delay , um Latenz oder langsame Netzwerkaufrufe zu simulieren. Wenn die Verzögerung auf 500 Millisekunden festgelegt ist, dauert die asynchrone PWGasync Methode etwas mehr als 500 Millisekunden, während die synchrone PWG Version über 1.500 Millisekunden dauert. Die synchrone PWG Methode wird im folgenden Code angezeigt.
public ActionResult PWG()
{
ViewBag.SyncType = "Synchronous";
var widgetService = new WidgetService();
var prodService = new ProductService();
var gizmoService = new GizmoService();
var pwgVM = new ProdGizWidgetVM(
widgetService.GetWidgets(),
prodService.GetProducts(),
gizmoService.GetGizmos()
);
return View("PWG", pwgVM);
}
Die asynchrone PWGasync Methode wird im folgenden Code angezeigt.
public async Task<ActionResult> PWGasync()
{
ViewBag.SyncType = "Asynchronous";
var widgetService = new WidgetService();
var prodService = new ProductService();
var gizmoService = new GizmoService();
var widgetTask = widgetService.GetWidgetsAsync();
var prodTask = prodService.GetProductsAsync();
var gizmoTask = gizmoService.GetGizmosAsync();
await Task.WhenAll(widgetTask, prodTask, gizmoTask);
var pwgVM = new ProdGizWidgetVM(
widgetTask.Result,
prodTask.Result,
gizmoTask.Result
);
return View("PWG", pwgVM);
}
Die folgende Abbildung zeigt die von der PWGasync-Methode zurückgegebene Ansicht.
Verwendung eines Abbruch-Tokens
Asynchrone Aktionsmethoden, die Task<ActionResult> zurückgeben, sind abbrechbar, d. h. sie nehmen einen CancellationToken-Parameter an, wenn einer mit dem AsyncTimeout-Attribut bereitgestellt wird. Der folgende Code zeigt die GizmosCancelAsync Methode mit einem Timeout von 150 Millisekunden.
[AsyncTimeout(150)]
[HandleError(ExceptionType = typeof(TimeoutException),
View = "TimeoutError")]
public async Task<ActionResult> GizmosCancelAsync(
CancellationToken cancellationToken )
{
ViewBag.SyncOrAsync = "Asynchronous";
var gizmoService = new GizmoService();
return View("Gizmos",
await gizmoService.GetGizmosAsync(cancellationToken));
}
Der folgende Code zeigt die GetGizmosAsync-Überladung, die einen CancellationToken-Parameter akzeptiert.
public async Task<List<Gizmo>> GetGizmosAsync(string uri,
CancellationToken cancelToken = default(CancellationToken))
{
using (HttpClient httpClient = new HttpClient())
{
var response = await httpClient.GetAsync(uri, cancelToken);
return (await response.Content.ReadAsAsync<List<Gizmo>>());
}
}
In der bereitgestellten Beispielanwendung ruft die Auswahl des Links "Abbruchtokendemo" die Methode GizmosCancelAsync auf und veranschaulicht den Abbruch des asynchronen Aufrufs.
Serverkonfiguration für Webdienstaufrufe mit hoher Parallelität/hoher Latenz
Um die Vorteile einer asynchronen Webanwendung zu erkennen, müssen Sie möglicherweise einige Änderungen an der Standardserverkonfiguration vornehmen. Beachten Sie beim Konfigurieren und Stresstest Ihrer asynchronen Webanwendung Folgendes.
Windows 7, Windows Vista und alle Windows-Clientbetriebssysteme verfügen über maximal 10 gleichzeitige Anforderungen. Sie benötigen ein Windows Server-Betriebssystem, um die Vorteile asynchroner Methoden unter hoher Last zu sehen.
Registrieren Sie .NET 4.5 mit IIS über eine Eingabeaufforderung mit erhöhten Rechten:
%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_regiis -i
Siehe ASP.NET IIS-Registrierungstool (Aspnet_regiis.exe)Möglicherweise müssen Sie den HTTP.sys Warteschlangengrenzwert von 1.000 auf 5.000 erhöhen. Wenn die Einstellung zu niedrig ist, wird möglicherweise HTTP.sys Anforderungen mit einem HTTP-503-Status ablehnen. So ändern Sie den HTTP.sys Warteschlangengrenzwert:
- Öffnen Sie den IIS-Manager, und navigieren Sie zum Bereich "Anwendungspools".
- Klicken Sie mit der rechten Maustaste auf den Zielanwendungspool, und wählen Sie "Erweiterte Einstellungen" aus.
- Ändern Sie im Dialogfeld "Erweiterte Einstellungen " die Warteschlangenlänge von 1.000 auf 5.000.
Beachten Sie in den obigen Bildern, dass .NET Framework als v4.0 aufgeführt ist, auch wenn der Anwendungspool .NET 4.5 verwendet. Um diese Diskrepanz zu verstehen, sehen Sie sich Folgendes an:
Wenn Ihre Anwendung Webdienste verwendet oder System.NET, um mit einem Back-End über HTTP zu kommunizieren, müssen Sie möglicherweise das connectionManagement/maxconnection-Element erhöhen. Für ASP.NET Anwendungen ist dies durch das AutoConfig-Feature auf 12 mal die Anzahl der CPUs beschränkt. Das bedeutet, dass Sie auf einem Quad-Proc höchstens 12 * 4 = 48 gleichzeitige Verbindungen zu einem IP-Endpunkt haben können. Da dies an autoConfig gebunden ist, besteht die einfachste Möglichkeit,
maxconnectionin einer ASP.NET-Anwendung zu erhöhen, darin, System.Net.ServicePointManager.DefaultConnectionLimit programmatisch in derApplication_Start-Methode in der Datei "global.asax" festzulegen. Ein Beispiel finden Sie im Beispieldownload.In .NET 4.5 sollte der Standardwert von 5000 für MaxConcurrentRequestsPerCPU einwandfrei sein.