2012-11-23 15 views
16

Sono nuovo alla funzione asincrona di C# 5. sto cercando di capire la differenza tra queste due implementazioni:Async-await Task.Run vs HttpClient.GetAsync

Attuazione 1:

private void Start() 
{ 
    foreach(var url in urls) 
    { 
     ParseHtml(url); 
    } 
} 

private async void ParseHtml(string url) 
{ 
    var query = BuildQuery(url); //BuildQuery is some helper method 
    var html = await DownloadHtml(query); 
    //... 
    MyType parsedItem = ParseHtml(html); 
    SaveTypeToDB(parsedItem); 
} 

private async Task<string> DownloadHtml(string query) 
{ 
    using (var client = new HttpClient()) 
    try 
    { 
     var response = await client.GetAsync(query); 
     return (await response.Content.ReadAsAsync<string>()); 
    } 
    catch (Exception ex) 
    { 
     Logger.Error(msg, ex); 
     return null; 
    } 
} 

Attuazione 2:

private void DoLoop() 
{ 
    foreach(var url in urls) 
    { 
     Start(url); 
    } 
} 

private async void Start(url) 
{ 
    await Task.Run(() => ParseHtml(url)) ; 
} 

private void ParseHtml(string url) 
{ 
    var query = BuildQuery(url); //BuildQuery is some helper method 
    var html = DownloadHtml(query); 
    //... 
    MyType parsedItem = ParseHtml(html); 
    SaveTypeToDB(parsedItem); 
} 

private string DownloadHtml(string query) 
{ 
    using (var client = new WebClient()) 
    { 
     try 
     { 
      return client.DownloadString(query); 
     } 
     catch (Exception ex) 
     { 
      Logger.Error(msg, ex); 
      return null; 
     } 
    } 
} 

Preferisco utilizzare la seconda implementazione in quanto richiederà meno firme 'asincrone' sui metodi nel mio codice. Sto cercando di capire qual è il vantaggio dell'utilizzo della classe HttpClient rispetto all'utilizzo di una nuova attività e in attesa invece?

C'è qualche differenza tra le due implementazioni?

+0

"firme asincrone" - non preoccuparti di loro. "async" è solo un "marker" per rimanere compatibile con il vecchio codice. In pre-5.0 potevi avere variabili/funzioni con il nome "await". Ma poiché i vecchi metodi non hanno la parola chiave "async", il codice sarà comunque compilato (senza modifiche) con C# 5.0. – igrimpe

risposta

28

Preferisco utilizzare la seconda implementazione in quanto richiede meno firme "asincrone" sui metodi nel mio codice.

Che suona come un molto giustificazione strano. Stai cercando di eseguire fondamentalmente "un po 'in modo asincrono" - quindi perché non farlo chiaro?

C'è qualche differenza tra le due implementazioni?

Assolutamente. La seconda implementazione legherà un thread mentre i blocchi WebClient.DownloadString, in attesa che la richiesta venga completata. La prima versione non ha thread bloccati - si basa su una continuazione da attivare al termine della richiesta.

Inoltre, considerare la chiamata Logger.Error. Nella versione asincrona, che verrà comunque eseguita nel contesto del codice di chiamata originale. Quindi, se questa è, per esempio, un'interfaccia utente di Windows Form, sarai ancora presente nel thread dell'interfaccia utente e potrai accedere agli elementi dell'interfaccia utente, ecc. Nella seconda versione, eseguirai un thread pool di thread e avresti bisogno per eseguire il marshalling sul thread dell'interfaccia utente per aggiornare l'interfaccia utente.

Si noti che il proprio metodo async void quasi certamente non deve essereasync void. È consigliabile eseguire un metodo async solo per il ritorno alle firme del gestore di eventi void. In tutti gli altri casi, tornare Task - in questo modo il chiamante può vedere quando il vostro compito è finito, gestire le eccezioni ecc

Si noti inoltre che non è necessario utilizzare HttpClient per asincronia - si potrebbe usare WebClient.DownloadStringTaskAsync invece, così il vostro ultimo metodo potrebbe diventare:

private async Task<string> DownloadHtml(string query) 
{ 
    using (var client = new WebClient()) 
    { 
     try 
     { 
      return await client.DownloadStringTaskAsync(query); 
     } 
     catch (Exception ex) 
     { 
      Logger.Error(msg, ex); 
      return null; 
     } 
    } 
} 
+0

Grazie! Domanda veloce però. Prendiamo ParseHtml nella prima implementazione. Come restituire l'oggetto Task propagato da DownloadHtml? Quando uso il suo valore viene convertito in "MyType" – vondip

+1

@vondip: non capisco cosa intendi, temo. Forse chiederlo più in dettaglio come una domanda separata? –

6

per le applicazioni server, async è di minimizzare il numero di thread bloccati hai: aumentando l'efficienza del pool di thread e, forse, permettendo che il vostro programma di scalare a più utenti.

Per le applicazioni client in cui è improbabile che ci si debba preoccupare del conteggio dei thread, async fornisce un modo relativamente semplice per mantenere fluido l'interfaccia utente quando si esegue l'I/O.

È molto diverso da Task.Run sotto il cofano.

1

È possibile trarre vantaggio dall'elaborazione asincrona solo se il thread chiamante ha qualcosa di significativo da fare, ad esempio mantenere l'interfaccia utente reattiva. Se il thread chiamante avvia solo un'attività e non fa altro che attendere fino al completamento dell'attività, il processo verrà eseguito più lentamente.

La tua seconda implementazione inizia un'attività, ma attendi che finisca senza fare altro. In questo modo non ne trarrai beneficio.

Non descrivere il proprio ambiente, ma se si dispone di un'interfaccia utente che deve tenere reattivo, poi la realizzazione del metodo 1 è ok, tranne che il vostro Start() non è dichiarato asincrona e non aspetta:

private async Task StartAsync() 
{ 
    foreach (var url in urls) 
    { 
     await ParseHtml(url) 
    } 
} 

È possibile chiamare questo da un gestore di eventi come segue:

private async void OnButton1_clicked(object sender, ...) 
{ 
    await StartAsync(); 
} 

Nota: il ParseHtml ha è preceduto da attendono. Il prossimo html verrà analizzato al termine dell'analisi precedente. Tuttavia, poiché l'analisi è asincrona, il thread chiamante (il thread dell'interfaccia utente?) Sarà in grado di eseguire altre operazioni come rispondere all'input dell'utente.

Tuttavia, se la funzione parseHTML è in grado di eseguire simultaneamente il seguente codice sarebbe preferibile, e probabilmente più veloce:

private async Task StartAsync() 
{ 
    var tasks = new List<Task>() 
    foreach (var url in urls) 
    { 
     tasks.Add(ParseHtml(url)); 
    } 
    // now you have a sequence of Tasks scheduled to be run, possibly simultaneously. 
    // you can do some other processing here 
    // once you need to be certain that all tasks are finished await Task.WhenAll(...) 
    await Task.WhenAll(tasks); 
    // Task.WhenAls(...) returns a Task, hence you can await for it 
    // the return of the await is a void 
} 
  • Se si chiama una funzione che restituisce una Task, è possibile continuare a fare altri cose mentre l'attività è in esecuzione o in attesa che l'attività termini e utilizza i risultati dell'attività.
  • in attesa, il codice si interrompe, ma i chiamanti continuano l'elaborazione finché non sono in attesa del completamento dell'attività
  • se la procedura è asincrona è possibile solo attendere nella procedura.
  • funzioni asincrone possono essere chiamati solo da altre funzioni asincrone, o chiamando Task.Run (() => ...) o, se si preferisce: Task.Factory.StartNew (() => ...)
  • Invece di vuoto una funzione asincrona ritorna Task
  • Invece di TResult un rendimento funzione asincrona Task <TResult>
  • l'unica eccezione è il gestore di eventi: dichiararla asincrono e restituire void.
  • Se è necessario completare l'attività, attendere l'attività.
  • Il ritorno dell'attesa è il TResult.
Problemi correlati