2015-07-23 15 views
5

Sto cercando di mettere la testa in asincrono/aspettare e ho pensato di capire alcune cose sull'utilizzo. Ma ancora non abbastanza chiaro quale sarebbe il vantaggio effettivo in uno scenario come di seguito.Che differenza fa - eseguire un'azione delegata "asincrona" con un Task.Run (vs delegato azione predefinito)?

Esaminare l'attività. Utilizzo. Il primo metodo utilizza un delegato normale e utilizza Thread.Sleep ma il secondo utilizza delegato 'async' e Task.Delay.

La mia domanda è: in che modo questo fa alcuna differenza con questo metodo (o non lo fa)?

Il metodo stesso è un metodo asincrono. Il codice sta creando un thread separato (tramite Task.Run) e questo thread non ha nient'altro da fare se non eseguire quel delegato. Quindi, anche se produce un attese su Task.Delay, qual è l'utilizzo in questo scenario, poiché il thread è comunque un thread isolato non utilizzato per nient'altro e anche se utilizza solo Thread.Sleep, il thread sarebbe comunque in contesto passare a produrre altri thread per il processore.

// 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; 
     })); 
} 

risposta

5

La mia domanda è: come fa questo fa alcuna differenza a questo metodo (o non lo fa)?

un paio di differenze

  1. Utilizzando un async delegato all'interno Task.Run significa che in realtà esegue un Task<Task>. Questo è nascosto da voi per il fatto che Task.Run è asincrona consapevole e scarta il compito interna per voi, qualcosa che non ha fatto Task.Factory.StartNew
  2. Quando si utilizza un delegato asincrono con Task.Run, si crea un nuovo thread, poi cedere il controllo una volta che hai colpito await Task.Delay. La continuazione verrà eseguita su un thread thread-pool arbitrario. In aggiunta, il delegato viene trasformato in una macchina a stati dal compilatore.

    Con il delegato normale, si crea un thread, lo si blocca in modo sincrono per 5 secondi e quindi si prosegue nel punto in cui era stato interrotto. Niente macchine statali, niente cedimenti.


Quindi, anche se si produce con un'attendono Task.Delay, che cosa è l'uso in questo scenario, dal momento che il thread è in ogni modo un filo isolato non utilizzati per altri scopi e anche se utilizza solo Thread.Sleep, il thread farebbe comunque il cambio di contesto a produrre altri thread per il processore .

L'uso di async con Task.Run può essere quando si vuole fare entrambe le CPU e il lavoro IO legato, il tutto in un thread dedicato. Hai ragione nel pensare che dopo che il delegato asincrono si è reso, ritorna su un filo arbitrario. Tuttavia, se non si fosse utilizzato Task.Run e il metodo async eseguito da un thread con un contesto di sincronizzazione personalizzato collegato (come WinformsSynchronizationContext), qualsiasi lavoro successivo allo await restituirebbe al ciclo di messaggio dell'interfaccia utente, a meno che non si utilizzasse ConfigureAwait(false).

A dire la verità, non ho visto molti scenari in cui Task.Run e async vengono utilizzati correttamente. Ma a volte ha senso.

3

La differenza è che si sta sprecando un thread e il relativo intervallo di tempo assegnato.

Quando si blocca un thread per 5 secondi, il thread non può essere utilizzato in altre parti del sistema per eseguire il lavoro effettivo della CPU. Crea anche un interruttore di contesto in quanto quel thread non può fare nient'altro.

Quando si libera quel thread utilizzando Task.Delay anziché Thread.Sleep, il thread può tornare allo ThreadPool, eseguire un'operazione di attesa ed eseguirlo.

Quando si liberano i thread, è possibile rendere l'applicazione più scalabile ed efficiente in quanto richiede meno risorse per eseguire lo stesso lavoro o la stessa quantità di risorse per fare più lavoro.


Quando il funzionamento è davvero asincrono non c'è alcuna necessità di utilizzare Task.Run (a meno che non avete bisogno di un thread in background). Si può solo chiamare tale metodo e attendere il compito restituita:

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

Grazie, è utile. Un po 'fuori strada da async ma in questo esempio, ho creato una nuova discussione (con Task.Run poiché "Smtphost.Send" è una chiamata di I/O bloccata.) Quindi, è logico scaricarla su una thread separata. pensi che questo non faccia alcuna differenza? –

+0

@EverythingMatters 'Task.Run' non crea un thread, usa un thread' ThreadPool'. Puoi farlo se vuoi scaricare il lavoro su un thread diverso (questo è utile per esempio per sbloccare il thread della GUI) ma ciò non migliora le prestazioni Se il thread originale è un thread 'ThreadPool', il blocco di un altro thread non aggiunge alcun valore. – i3arnon