Perché il CTP asincrono funziona male?

Non capisco davvero perché l’ await e l’ async non migliorino le prestazioni del mio codice qui come dovrebbero .

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.
( Mi rendo conto che await e async non creano thread separati, tuttavia, il sistema operativo dovrebbe eseguire i download in parallelo e richiamare il mio codice nel thread originale, non dovrebbe? )

Sto usando async e await impropriamente? 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 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); } } } 

Produzione:

 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?? 

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

Come regola generale, è meglio non utilizzare le continuazioni delle attività se si è in await / async disponibili. Inoltre, non utilizzare WaitAny / WaitAll – gli equivalenti async sono WhenAny e WhenAll .

Lo scriverei così:

 static async Task SumPageSizesAsync(IEnumerable uris) { // Start one Task 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); } 

Potrei leggere male il tuo codice, ma sembra che tu stia avviando un thread in background per eseguire la lettura asincrona e quindi bloccarlo immediatamente, in attesa che venga completato. Nulla sulla porzione ‘asincrona’ del tuo codice è in realtà asincrono. Prova questo:

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

E usalo così:

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

Potrei sbagliarmi: la roba asincrona è nuova e non ne sono al corrente al 100% – ma il timing simile alla versione di sincronizzazione sembra darmi una risposta.