2014-02-25 7 views
8

Ich wickle AspNet.Identity. Aber etwas verwirrt mich über TPL.Was ist der Hauptunterschied zwischen dem Zurückgeben einer Aufgabe durch das await-Schlüsselwort und dem Zurückgeben ohne awaise-Schlüsselwort?

Erstes Beispiel:

public virtual async Task<IdentityResult> RemovePasswordAsync(string userId) 
    { 
     var user = _store.FindByIdAsync(userId).Result; 
     if (user == null) 
      throw new InstanceNotFoundException("user"); 

     user.PasswordHash = String.Empty; 
     user.SecurityStamp = String.Empty; 
     return await UpdateAsync(user); 
    } 

    public virtual async Task<IdentityResult> UpdateAsync(TUser user) 
    { 
     await _store.UpdateAsync(user); 
     return new IdentityResult(); 
    } 

Zweites Beispiel:

public virtual Task<IdentityResult> RemovePasswordAsync(string userId) 
    { 
     var user = _store.FindByIdAsync(userId).Result; 
     if (user == null) 
      throw new InstanceNotFoundException("user"); 

     user.PasswordHash = String.Empty; 
     user.SecurityStamp = String.Empty; 
     return UpdateAsync(user); 
    } 

    public virtual async Task<IdentityResult> UpdateAsync(TUser user) 
    { 
     await _store.UpdateAsync(user); 
     return new IdentityResult(); 
    } 

Und Client nennen dies wie:

result = await _userManager.RemovePasswordAsync(user.Id); 

Meine erste Frage ist:

Wenn der Client ruft die zweite an Methode wird die Arbeit aus dem IO-Thread in einen Threadpool-Thread ausgelagert. Und wenn RemovePasswordAsync aufgerufen wird, ruft es UpdateAsync auf, das ein Warte-Schlüsselwort hat. An diesem Punkt wird dieser Threadpool-Thread in einen anderen Threadpool-Thread ausgelagert. Oder verwendet TPL stattdessen den gleichen Thread?

Und meine zweite Frage ist; Was ist der Hauptunterschied zwischen der ersten Implementierung und der zweiten Implementierung der Konstruktion dieser asynchronen Methode?

EDIT:

Dies ist die Update-Methode der Klasse Users. (_store.UpdateAsync (Benutzer))

public Task UpdateAsync(TUser user) 
    { 
     if (user == null) 
      throw new ArgumentNullException("user"); 

     return _userService.UpdateAsync(user); 
    } 

Und das ist die Update-Methode der Klasse Userservice

public Task UpdateAsync(TUser user) 
    { 
     return Task.Factory.StartNew(() => Update(user)); 
    } 
+2

Die zweite Frage: http://StackOverflow.com/Q/21033150/1768303 – Noseratio

+3

Sie sollten StartNew (in Web-Anwendungen) eigentlich nicht verwenden - Der Grund dafür ist, dass wenn Sie warten, der ausführende Thread zurück an gesendet wird der CLR-Threadpool, wenn Sie neu beginnen, ziehen Sie einen Thread aus dem CLR-Threadpool. Dies ist in Webanwendungen kontraproduktiv. (Sie erhalten Thread-Verhungern, IIS kann einem Thread keine neuen Anforderungen zuweisen, sodass Ihre HTTP.Sys-Eingangswarteschlange (http-Stapel) voll wird und Ihre Benutzer nicht mehr zugelassen werden) –

+0

@Steve Thread-Verhungern? Wie? Sie befreien den Anfrage-Thread (so dass asp.net ihn verwenden kann, um andere eingehende Anfragen zu servern) und entlädt sich in einen Threadpool-Thread. – dcastro

Antwort

3

Ich werde Ihre erste Frage beantworten.

Sie missverstehen, wie async/abwarten funktioniert.

Eine async Methode wird synchron mindestens laufen, bis er die erste await Aussage trifft. Wenn es ein await trifft, hat es zwei Möglichkeiten:

  • Wenn die awaitable (z Task) bereits abgeschlossen ist, führt die Ausführung auf dem aktuellen Kontext (das heißt UI-Thread oder Kontext der ASP.NET-Anforderung).
  • Wenn das Erwartete noch nicht abgeschlossen ist, wird der übrige Text der Methode umgebrochen und die Ausführungszeit für den aktuellen Kontext (d. H. UI-Thread) (*) festgelegt, wenn die Aufgabe abgeschlossen ist.

Durch diese Definition wird der gesamte Code im Kontext der gleichen ASP.NET-Anfrage ausgeführt.

_store.UpdateAsync kann jedoch einen ThreadPool-Thread hervorbringen (z. B. mithilfe von Task.Run).

Aktualisiert

Nach Ihrer Antwort aktualisiert wird Update(user) auf einem Threadpool-Thread ausgeführt werden. Alles andere wird im aktuellen Kontext ausgeführt.


(*) Der Rest des Körpers des Verfahrens wird auf einem Thread Thread geplant werden, laufen wenn es keinen Kontext Synchronisation ist (das heißt, Konsolenanwendung).

+0

Ja, ich glaube, du hast recht, _store.UpdateAsync bewirkt, dass es zu einem anderen Thread auszulagern. Aber denken Sie an einem Verfahren, bei dem am Anfang habe ich einfach, dass die Asynchron-Methode wie folgt aufrufen: 'var removePasswordTask = RemovePasswordAsync (user.Id); // ...... // ...... var result = erwarten removePasswordTask; ' Da diese Aufgabe ist heiß, wird es den Moment, als ich es auf eine Aufgabe delegieren Variable ausgeführt werden. Von dem Moment an, wo ich es delegiere, bis zu dem Moment, wo ich es erwarte, wo findet die Hinrichtung statt? –

+1

@ KemalGültekin 1 hier einen Blick auf Gotcha # Nehmen: [Async in C# und F #: Asynchronous gotchas in C#] (http://tomasp.net/blog/csharp-async-gotchas.aspx/) – dcastro

+1

@ KemalGültekin Sie Selbst wenn Warten Sie nicht auf die von "RemovePasswordAsync" zurückgegebene Aufgabe, der Aufruf wird synchron ausgeführt, bis er auf "Task.Factory.StartNew" trifft. Dann wird 'Update (Benutzer)' in den ThreadPool ausgelagert und eine 'Task', die diesen Job darstellt, wird zurückgegeben. Diese Aufgabe wird aufgebläht, bis sie die Top-Level-Methode erreicht, die dann '// ... // ...' ausführt. Dann erwartet die Top-Level-Methode diese Aufgabe - der Rest Ihrer Methode (d. H. Was auch immer nach dem "Warten" kommt) wird für den späteren Kontext geplant. – dcastro

2

Und meine zweite Frage ist; Was ist der Hauptunterschied zwischen der ersten Implementierung und der zweiten Implementierung der Konstruktion dieser asynchronen Methode?

Ihre erste Implementierung und sollte durch Ersetzen der Blockierung _store.FindByIdAsync(userId).Result mit asynchronen await _store.FindByIdAsync(userId) verbessert werden:

public virtual async Task<IdentityResult> RemovePasswordAsync(string userId) 
{ 
    var user = await _store.FindByIdAsync(userId); 
    if (user == null) 
     throw new InstanceNotFoundException("user"); 

    user.PasswordHash = String.Empty; 
    user.SecurityStamp = String.Empty; 
    return await UpdateAsync(user); 
} 

Ohne eine solche Aktualisierung wird der Unterschied vielleicht am besten beschrieben durch Eric Lippert here. Eine besondere Sache ist how exceptions can possibly be thrown und behandelt.

Aktualisiert, um die Kommentare zu adressieren. Sie sollten nicht mit Task.Factory.StartNew oder Task.Run in ASP.NET entladen. Dies ist nicht eine UI-App, wo Sie die Benutzeroberfläche reagieren müssen. All dies fügt nur einen Overhead eines Thread-Schalters hinzu. Der HTTP-Request-Handler, der Task.Run aufruft, dann erwartet oder blockiert mindestens die gleiche Anzahl von Threads, die Sie ausführen müssen, Sie werden die Skalierbarkeit nicht verbessern und nur die Leistung beeinträchtigen. Auf der anderen Seite macht es Sinn, natürlich IO-gebundene Aufgaben zu verwenden, die keinen Thread während der Ausführung verwenden.

+0

Sie haben recht, ohne dass Sie darauf warten, sollte die Methode wegen der Überlastung nicht als async markiert werden. Aber ich habe eine andere Frage; wenn ich diese Aufgabe erwarte sie weiterhin synchroniously bis einem Task.Run oder Factory.StartNew etc .. So läuft, bis als sein den gleichen Faden, aber bis zur await Aussage zu diesem Punkt zu einem anderen Thread seines Offloading, die an diesem Punkt Rückkehr zum aufrufenden Thread. Also in diesem speziellen Beispiel; unter Verwendung Task.FromResult –

+0

@ KemalGültekin In diesem Fall ist es nicht besser, das async Stichwort zu entfernen und ruft FindByIdAsync.Result somit blockiert und am Ende die Aufgabe zurückkehren, '.Result' würde nicht blockieren, denn das Ergebnis wäre, sofort verfügbar, da das Ganze synchron lief. Sie können Aufgaben also genauso gut entfernen. – dcastro

+0

@dcastro Ja, Sie haben Recht, aber ich muss trotzdem Aufgaben verwenden, um die Schnittstelle zu erfüllen. –

Verwandte Themen