2014-09-18 3 views
7

Ich würde Ihre Hilfe im Folgenden benötigen. Seit fast einem Monat lese ich über Tasks und async.async Aufgabe <HttpResponseMessage> Get VS HttpResponseMessage Get

Ich wollte in einem einfachen wep api Projekt versuchen, meinen neuen erworben knowledege umzusetzen. Ich habe die folgenden Methoden und beide funktionieren wie erwartet:

Also die Frage. Ich habe versucht, Fiedler zu benutzen und zu sehen, was der Unterschied zwischen diesen beiden ist. Der asynchrone ist etwas schneller, aber was ist der wirkliche Vorteil bei der Implementierung von etwas Ähnlichem in einer Web-API?

+0

Well Zunächst einmal vielen Dank für die Antwort. Du hast mir einige "Hausaufgaben" zu tun gegeben. Ich verstehe, dass Factory.StartNew schrecklich ist, da es bei jeder Anfrage tatsächlich einen neuen Thread einleitet! Ich verstehe auch, dass die "asynchrone Operation" bis zum Repository weitergeleitet werden muss, also bis GetUsers (Service) -> GetUsersAsync (Repository). Also werden wir eine Kette von asynchronen Methoden haben, bis hin zum Repository. Das Repository müsste ein Modell implementieren, das asynchrone Operationen unterstützt, z. B. Asynchronous Ado.Net oder EntityFramework. –

+0

Wenn ich diese Web API erstellen würde, würde ich mit 3 Schichten beginnen: 1) Präsentation 2) Service und 3) Repository. Das Repository würde Async Ado.Net oder Entityframework implementieren. Darüber hinaus wird das alles auf dem Hauptthread jedoch mit ASYNCHRONOUS-Vorgängen geschehen. Ich habe versucht, die Theorie im folgenden Buch Multithreading in C# 5.0 Kochbuch "zu verschlingen". Es scheint, dass all dieses Wissen in meinem Kopf durcheinander ist, so Entschuldigungen, wenn Sie mich finden, Dinge zu wiederholen. Ich werde den Code veröffentlichen, der erklärt, was "ich denke" ich verstehe. Ich würde mich freuen, wenn Sie alle wieder so aktiv beitragen können !! –

Antwort

2

Es macht mehr Sinn, wo der Anruf mit wichtigen IO-Operationen stattfindet. Ja, Async ist schneller, da es den Anforderungsthread für die Zeit freigibt, in der die Operationen ausgeführt werden. Aus Sicht des Web-Servers geben Sie daher dem Pool einen Thread zurück, der vom Server für zukünftige Anrufe verwendet werden kann.

Also für z.B. Wenn Sie eine Suchoperation auf dem SQL-Server ausführen, möchten Sie möglicherweise async ausführen und den Leistungsvorteil sehen.

Es ist gut für die Skalierbarkeit, die mehrere Server beteiligt.

So z.B. Wenn das SearchRecordAsync sein SQL an die Datenbank sendet, gibt es eine unvollständige Aufgabe zurück, und wenn die Anfrage die Wartezeit erreicht, gibt sie das Anfragethread an den Thread-Pool zurück. Später, wenn die DB-Operation abgeschlossen ist, wird ein Anfrage-Thread aus dem Thread-Pool genommen und zum Fortsetzen der Anfrage verwendet.

Auch wenn Sie nicht verwenden, SQL-Operationen, lassen Sie sagen, dass Sie eine E-Mail zu 10 Personen senden möchten. In diesem Fall ist auch async sinnvoller.

Async ist auch sehr praktisch, um den Fortschritt eines langen Ereignisses anzuzeigen. So erhält der Benutzer immer noch die aktive GUI, während die Aufgabe im Hintergrund ausgeführt wird.

Um zu verstehen, sehen Sie sich bitte dieses Beispiel an.

Hier versuche ich eine Aufgabe namens Mail senden zu initiieren. Interim Ich möchte die Datenbank aktualisieren, während der Hintergrund Mail-Aufgabe senden ausführt.

Sobald die Datenbank-Update passiert ist, es für die Sende Mail-Aufgabe wartet abgeschlossen sein. Bei diesem Ansatz ist es jedoch klar, dass ich eine Aufgabe im Hintergrund ausführen und trotzdem mit dem ursprünglichen (Haupt-) Thread fortfahren kann.

using System; 
using System.Threading; 
using System.Threading.Tasks; 

public class Program 
{ 
    public static void Main() 
    { 
     Console.WriteLine("Starting Send Mail Async Task"); 
     Task task = new Task(SendMessage); 
     task.Start(); 
     Console.WriteLine("Update Database"); 
     UpdateDatabase(); 

     while (true) 
     { 
      // dummy wait for background send mail. 
      if (task.Status == TaskStatus.RanToCompletion) 
      { 
       break; 
      } 
     } 

    } 

    public static async void SendMessage() 
    { 
     // Calls to TaskOfTResult_MethodAsync 
     Task<bool> returnedTaskTResult = MailSenderAsync(); 
     bool result = await returnedTaskTResult; 

     if (result) 
     { 
      UpdateDatabase(); 
     } 

     Console.WriteLine("Mail Sent!"); 
    } 

    private static void UpdateDatabase() 
    { 
     for (var i = 1; i < 1000; i++) ; 
     Console.WriteLine("Database Updated!"); 
    } 

    private static async Task<bool> MailSenderAsync() 
    { 
     Console.WriteLine("Send Mail Start."); 
     for (var i = 1; i < 1000000000; i++) ; 
     return true; 
    } 
} 
+0

Vielen Dank für die Antwort.Sagen wir, wir wollen eine Web-API erstellen, die so viele Anfragen wie möglich pro Minute für einen E-Commerce mit hohem Volumen erfüllen muss. Wir haben keine IO-Operationen, aber nehmen wir an, dass wir einige teure SQL-Operationen haben. Wenn ich meine Hauptendpunkte als async dekorieren würde, wäre das ein guter Vorschlag? –

+0

Hey Kumpel, ich habe mit dem einfachen Beispiel versucht, was ich mit async meinte, es ist das gleiche wie Threading, aber es ist weniger Kopfschmerzen, weil Compiler alles für uns tut. – codebased

0

Mit async auf Ihrem Server kann Skalierbarkeit dramatisch verbessern, da sie den Faden der Anforderung andere Anforderungen zu verarbeiten dient entlastet, während die async Operation im Gange ist. Zum Beispiel würde in einem synchronen E/A-Betrieb der Thread ausgesetzt werden und nichts tun, bis der Vorgang abgeschlossen ist und nicht verfügbar wäre, um eine weitere Anfrage zu bedienen.

Das heißt, mit Task.Factory.StartNewstartet ein anderer Thread, so dass Sie die Skalierbarkeit Vorteile überhaupt nicht erhalten. Ihr ursprünglicher Thread kann wiederverwendet werden, aber Sie haben die Arbeit in einen anderen Thread ausgelagert, sodass es überhaupt keinen Nettovorteil gibt. Tatsächlich kostet es einen Wechsel zu einem anderen Thread, aber das ist minimal.

Wirklich asynchronen Operationen starten einen Thread nicht und ich würde sehen, ob eine solche Operation existiert, oder wenn man für Request.CreateResponse geschrieben werden kann. Dann wäre Ihr Code viel besser skalierbar. Wenn nicht, sind Sie besser dran mit dem synchronen Ansatz.

21

Wie andere darauf hingewiesen haben, ist der Punkt async auf ASP.NET, dass es einen der ASP.NET-Thread-Pool-Threads freigibt. Dies funktioniert hervorragend für natürlich-asynchrone Operationen wie E/A-gebundene Operationen, weil das ein Thread weniger auf dem Server ist (es gibt keinen Thread, der die asynchrone Operation "as I explain on my blog" verarbeitet). Somit ist der primäre Vorteil von async auf der Serverseite Skalierbarkeit.

Sie möchten jedoch vermeiden, Task.Run (und, noch schlimmer, Task.Factory.StartNew) auf ASP.NET. Ich nenne dies "falsche Asynchronität", weil sie nur synchrone/blockierende Arbeit an einem Thread-Pool-Thread ausführen. Sie sind nützlich in UI-Apps, in denen Sie den UI-Thread abarbeiten möchten, damit die Benutzeroberfläche reaktionsfähig bleibt. Sie sollten jedoch (fast) nie in ASP.NET oder anderen Server-Apps verwendet werden.

Die Verwendung von Task.Run oder Task.Factory.StartNew auf ASP.NET verringert die Skalierbarkeit. Sie werden einige unnötige Thread-Schalter verursachen. Bei länger andauernden Vorgängen könnten Sie die ASP.NET-Thread-Pool-Heuristik beenden, wodurch zusätzliche Threads erstellt und später unnötigerweise zerstört werden. Ich lese diese Leistungsprobleme Schritt-für-Schritt in another blog post.

Also müssen Sie darüber nachdenken, was jede Aktion macht, und ob sollte async sein. Wenn dies der Fall ist, sollte diese Aktion asynchron sein. In Ihrem Fall:

public HttpResponseMessage Get() 
{ 
    var data = _userServices.GetUsers(); 
    return Request.CreateResponse(HttpStatusCode.OK, data); 
} 

Was genau ist Request.CreateResponse tun? Es wird nur ein Antwortobjekt erstellt. Das ist es - nur eine schicke new. Es gibt keine I/O, und es ist sicherlich nicht etwas, das zu einem Hintergrund-Thread weggeschoben werden muss.

Allerdings ist GetUsers viel interessanter. Das klingt eher wie ein Daten lesen, die ist I/O-basiert. Wenn Ihr Backend skalieren (zB Azure SQL/Tabellen/etc), dann sollten Sie zu machen, schauen, dass async zuerst, und sobald Ihr Service ist ein GetUsersAsync aussetzt, dann könnte diese Aktion async werden:

public async Task<HttpResponseMessage> Get() 
{ 
    var data = await _userServices.GetUsersAsync(); 
    return Request.CreateResponse(HttpStatusCode.OK, data); 
}