2012-01-15 12 views
6

Non capisco perché await e async non migliorino le prestazioni del mio codice qui like they're supposed to.Perché il CTP asincrono sta funzionando male?

Sebbene scettico, pensavo che il compilatore avrebbe dovuto riscrivere il mio metodo in modo che i download fossero eseguiti in parallelo ... ma sembra che non stia realmente accadendo.
(ho faccio si rendono conto che await e async non creano thread separati,? Tuttavia, il sistema operativo dovrebbe fare i download in parallal, e richiamata mio codice nel thread originale - non dovrebbe esso)

Sto usando async e await in modo errato? Qual è il modo corretto di usarli?

Codice:

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

static class Program 
{ 
    static int SumPageSizesSync(string[] uris) 
    { 
     int total = 0; 
     var wc = new WebClient(); 
     foreach (var uri in uris) 
     { 
      total += wc.DownloadData(uri).Length; 
      Console.WriteLine("Received synchronized data..."); 
     } 
     return total; 
    } 

    static async Task<int> SumPageSizesAsync(string[] uris) 
    { 
     int total = 0; 
     var wc = new WebClient(); 
     foreach (var uri in uris) 
     { 
      var data = await wc.DownloadDataTaskAsync(uri); 
      Console.WriteLine("Received async'd CTP data..."); 
      total += data.Length; 
     } 
     return total; 
    } 

    static int SumPageSizesManual(string[] uris) 
    { 
     int total = 0; 
     int remaining = 0; 
     foreach (var uri in uris) 
     { 
      Interlocked.Increment(ref remaining); 
      var wc = new WebClient(); 
      wc.DownloadDataCompleted += (s, e) => 
      { 
       Console.WriteLine("Received manually async data..."); 
       Interlocked.Add(ref total, e.Result.Length); 
       Interlocked.Decrement(ref remaining); 
      }; 
      wc.DownloadDataAsync(new Uri(uri)); 
     } 
     while (remaining > 0) { Thread.Sleep(25); } 
     return total; 
    } 

    static void Main(string[] args) 
    { 
     var uris = new string[] 
     { 
      // Just found a slow site, to demonstrate the problem :) 
      "http://www.europeanchamber.com.cn/view/home", 
      "http://www.europeanchamber.com.cn/view/home", 
      "http://www.europeanchamber.com.cn/view/home", 
      "http://www.europeanchamber.com.cn/view/home", 
      "http://www.europeanchamber.com.cn/view/home", 
     }; 
     { 
      var start = Environment.TickCount; 
      SumPageSizesSync(uris); 
      Console.WriteLine("Synchronous: {0} milliseconds", Environment.TickCount - start); 
     } 
     { 
      var start = Environment.TickCount; 
      SumPageSizesManual(uris); 
      Console.WriteLine("Manual: {0} milliseconds", Environment.TickCount - start); 
     } 
     { 
      var start = Environment.TickCount; 
      SumPageSizesAsync(uris).Wait(); 
      Console.WriteLine("Async CTP: {0} milliseconds", Environment.TickCount - start); 
     } 
    } 
} 

uscita:

Received synchronized data... 
Received synchronized data... 
Received synchronized data... 
Received synchronized data... 
Received synchronized data... 
Synchronous: 14336 milliseconds 
Received manually async data... 
Received manually async data... 
Received manually async data... 
Received manually async data... 
Received manually async data... 
Manual: 8627 milliseconds   // Almost twice as fast... 
Received async'd CTP data... 
Received async'd CTP data... 
Received async'd CTP data... 
Received async'd CTP data... 
Received async'd CTP data... 
Async CTP: 13073 milliseconds  // Why so slow?? 

risposta

10

La risposta di Chris è quasi corretta, ma introduce una condizione di competizione e blocca in modo sincrono tutte le attività.

Come regola generale, è preferibile non utilizzare la continuazione delle attività se è disponibile await/async. Inoltre, non utilizzare WaitAny/WaitAll - gli equivalenti sono WhenAny e WhenAll.

vorrei scrivere in questo modo:

static async Task<int> SumPageSizesAsync(IEnumerable<string> uris) 
{ 
    // Start one Task<byte[]> for each download. 
    var tasks = uris.Select(uri => new WebClient().DownloadDataTaskAsync(uri)); 

    // Asynchronously wait for them all to complete. 
    var results = await TaskEx.WhenAll(tasks); 

    // Calculate the sum. 
    return results.Sum(result => result.Length); 
} 
+0

Oh huh, sembra molto più pulito. =) Grazie! – Mehrdad

1

I può essere travisamento il codice, ma sembra che si sta lanciando un thread in background per fare la lettura asincrona e subito dopo il blocco, in attesa per il completamento. Nulla sulla porzione 'asincrona' del tuo codice è in realtà asincrono. Prova questo:

static async Task<int> SumPageSizesAsync(string[] uris) 
{ 
    int total = 0; 
    var wc = new WebClient(); 
    var tasks = new List<Task<byte[]>>(); 
    foreach (var uri in uris) 
    { 
     tasks 
      .Add(wc.DownloadDataTaskAsync(uri).ContinueWith(() => { total += data.Length; 
     })); 
    } 
    Task.WaitAll(tasks); 
    return total; 
} 

e utilizzarlo così:

{ 
     var start = Environment.TickCount; 
     await SumPageSizesAsync(uris); 
     Console.WriteLine("Async CTP: {0} milliseconds", Environment.TickCount - start); 
    } 

potrei essere sbagliato- la roba asincrona è nuova e io non sono a conoscenza al 100% con it- ma i tempi simile alla sincronizzazione la versione sembra darmi una spinta.

+0

Hm ... quindi credo che la questione logica è allora, qual è la corretta * * modo per farlo? – Mehrdad

+0

Modificato per aggiungere il modo in cui penso che dovrebbe funzionare. Di nuovo, potrebbe essere totalmente sbagliato. –

+0

Grazie! Sebbene pensassi che l'aspetto di "attendere" era quello di cambiare il resto del metodo in uno stile di passaggio continuo (e il punto centrale del CTP asincrono di eliminare la necessità di così tanto lavoro idraulico con i delegati/lambda) ... così come è diverso da eg 'BeginInvoke' e quant'altro, che avevamo già da almeno .NET 2.0? – Mehrdad

Problemi correlati