2015-07-23 2 views
5

Ich versuche, meinen Kopf async/warten und dachte, dass ich nur wenige Dinge über die Verwendung verstanden habe. Aber immer noch nicht ganz klar, was der eigentliche Vorteil in einem Szenario wie unten wäre.Welchen Unterschied macht es - Ausführen eines 'asynchronen' Aktionsdelegaten mit einer Task.Run (vs. Standard-Aktionsdelegate)?

Schauen Sie sich die Task.Run an. Die erste Methode verwendet einen normalen Delegaten und verwendet Thread.Sleep, aber der zweite verwendet 'asynchronen' Delegaten und Task.Delay.

Meine Frage ist: Wie macht dies einen Unterschied zu dieser Methode (oder es nicht)?

Die Methode selbst ist eine asynchrone Methode. Der Code erstellt einen separaten Thread (über Task.Run) und dieser Thread hat nichts anderes zu tun, als diesen Delegaten auszuführen. Also, selbst wenn es mit einer Wartezeit auf Task.Delay nachgibt, was ist die Verwendung in diesem Szenario, da der Thread sowieso ein isolierter Thread ist, der für nichts anderes verwendet wird und selbst wenn er nur Thread.Sleep verwendet, würde der Thread noch Kontext haben Wechseln Sie zu anderen Threads für den Prozessor.

// The task thread uses a async delegate 
public async Task<bool> RetrySendEmail(MailMessage message) 
{ 
     bool emailSent = false; 
     await (Task.Run(***async()*** => 
     { 
      for (int i = 0; i < 3; i++) 
      { 
       if (emailSent) 
         break; 
       else 
         // Wait for 5 secs before trying again 
         ***await Task.Delay(5000);*** 

       try 
       { 
         Smtphost.Send(message); 
         emailSent = true; 
         break; 
       } 
       catch (Exception e) { emailSent = false; // log; } 
      } 
      return emailSent; 
     })); 
} 

// The task thread uses a normal delegate 
public async Task<bool> RetrySendEmail(MailMessage message) 
{ 
     bool emailSent = false; 
     await (Task.Run(***()*** => 
     { 
      for (int i = 0; i < 3; i++) 
      { 
       if (emailSent) 
         break; 
       else 
         // Wait for 5 secs before trying again 
         ***Thread.Sleep(5000);*** 

       try 
       { 
         Smtphost.Send(message); 
         emailSent = true; 
         break; 
       } 
       catch (Exception e){ emailSent = false; // log; } 
      } 
       return emailSent; 
     })); 
} 

Antwort

5

Meine Frage ist: Wie dies einen Unterschied zu dieser Methode macht (oder es nicht)?

Ein paar Unterschiede bedeutet

  1. einen async Delegierten innerhalb Task.Run verwenden, dass Sie tatsächlich ein Task<Task> laufen. Dies ist versteckt von Ihnen durch die Tatsache, dass Task.Run async bewusst ist und packt die innere Aufgabe für Sie, etwas, das Task.Factory.StartNew nicht
  2. tat Wenn Sie einen Asynchron-Delegierten mit Task.Run verwenden, können Sie einen neuen Thread erstellen, dann die Kontrolle ergeben Sobald Sie await Task.Delay drücken. Die Fortsetzung wird auf einem beliebigen Thread-Pool-Thread ausgeführt. Außerdem wird der Delegat vom Compiler in eine Zustandsmaschine umgewandelt.

    Mit dem normalen Delegaten erstellen Sie einen Thread, blockieren ihn für 5 Sekunden synchron und fahren dann an der Stelle fort, an der Sie aufgehört haben. Keine Staatsmaschinen, kein Nachgeben.


Also, auch wenn es ergibt dich mit einem await auf Task.Delay, was die Verwendung in diesem Szenario, da der Faden sowieso ein isolierter Faden ist nicht für etwas anderes und auch verwendet Wenn es nur Thread.Sleep verwendet, würde der Thread immer noch wechseln, um anderen Threads für den -Prozessor zu weichen.

Die Verwendung von async mit Task.Run sein kann, wenn Sie sowohl die CPU und IO gebunden Arbeit tun wollen, die alle in einem eigenen Thread. Sie haben recht, wenn Sie denken, dass nach dem asynchronen Delegaten ein beliebiger Thread zurückgegeben wird. Wenn Sie jedoch Task.Run und die async -Methode nicht aus einem Thread ausgeführt haben, an den ein benutzerdefinierter Synchronisationskontext angehängt wurde (z. B. WinformsSynchronizationContext), würde jede Arbeit nach await der UI-Nachrichtenschleife zurückgeben, sofern Sie nicht ConfigureAwait(false) verwendet haben.

Um die Wahrheit zu sagen, habe ich nicht gesehen, dass viele Szenarien Task.Run und async richtig verwendet werden. Aber es macht manchmal Sinn.

3

Der Unterschied ist, dass Sie einen Thread und seine zugewiesene Zeitscheibe verschwenden.

Wenn Sie einen Thread für 5 Sekunden blockieren, kann dieser Thread nicht in anderen Teilen Ihres Systems verwendet werden, um die eigentliche CPU-Arbeit auszuführen. Es erstellt auch einen Kontextwechsel, da dieser Thread nichts anderes tun kann.

Wenn Sie diesen Thread freigeben, indem Sie Task.Delay statt Thread.Sleep verwenden, kann der Thread zum ThreadPool zurückkehren, eine wartende Aufgabe übernehmen und ausführen.

Wenn Sie Ihre Threads freigeben, können Sie Ihre Anwendung skalierbar und effizienter machen, da weniger Ressourcen für die gleiche Arbeit oder die gleiche Menge an Ressourcen für mehr Arbeit benötigt werden.


Wenn Ihr Betrieb wirklich asynchron es gibt keine Notwendigkeit Task.Run zu verwenden (wenn Sie einen Hintergrund-Thread benötigen). Sie können nur diese Methode aufrufen und warten auf die zurückgegebene Aufgabe:

public async Task<bool> RetrySendEmail(MailMessage message) 
{ 
    bool emailSent = false; 
    for (int i = 0; i < 3; i++) 
    { 
     if (emailSent) 
       break; 
     else 
       await Task.Delay(5000); 

     try 
     { 
       Smtphost.Send(message); 
       emailSent = true; 
       break; 
     } 
     catch (Exception e) { emailSent = false; // log; } 
    } 
    return emailSent; 
} 
+0

Vielen Dank, das ist hilfreich. Etwas abseits von Async, aber in diesem Beispiel habe ich einen neuen Thread erstellt (mit Task.Run, da 'Smtphost.Send' ein blockierender IO-Aufruf ist. Also dachte ich, es ist sinnvoll, das auf einen separaten Thread zu übertragen. Aber Sie denken, dass würde keinen Unterschied machen? –

+0

@ EverythingMatters 'Task.Run' erstellt keinen Thread, es verwendet einen' ThreadPool'-Thread.Das können Sie tun, wenn Sie Arbeit in einen anderen Thread entladen möchten (das ist nützlich um beispielsweise den GUI-Thread zu entsperren), aber das verbessert die Performance nicht.Wenn der ursprüngliche Thread ein 'ThreadPool'-Thread ist, fügt das Blockieren eines anderen Threads keinen Wert hinzu. – i3arnon

Verwandte Themen