2012-01-15 12 views
6

Naprawdę nie rozumiem, dlaczego await i async nie poprawiają wydajności mojego kodu tutaj like they're supposed to.Dlaczego asynchroniczne CTP działa słabo?

Chociaż jestem sceptyczny, pomyślałem, że kompilator powinien przerobić moją metodę, aby pobieranie było wykonywane równolegle ... ale wygląda na to, że tak naprawdę się nie dzieje.
(zrobić sobie sprawę, że await i async nie tworzyć osobne wątki;? Jednakże system operacyjny powinien być robienie pobrania w parallal i oddzwanianie mój kod w oryginalnym wątku - nie należy go)

Czy używam nieodpowiednio async i await? Jaki jest właściwy sposób ich użycia?

Kod:

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

wyjściowa:

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

Odpowiedz

10

Odpowiedź Chris jest prawie poprawna, ale wprowadza warunek wyścigu i synchronicznie blokuje wszystkie zadania.

Zgodnie z ogólną zasadą, najlepiej jest nie używać kontynuacji zadań, jeśli masz dostępne await/async. Nie używaj też równoważników WaitAny/WaitAll - są to i.

chciałbym napisać to tak:

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, wygląda to o wiele czystsze. =) Dzięki! – Mehrdad

1

I mogą być błędne odczytanie kodu, ale wygląda na to, uruchamiasz wątek tła zrobić odczyt asynchronicznej i natychmiast blokuje, czekając do jej zakończenia. Nic z "asynchronicznej" części twojego kodu jest tak naprawdę asynchroniczne. Spróbuj tego:

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

i używać go w ten sposób:

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

mogę być wrong- rzeczy asynchroniczny jest nowy i nie jestem 100% zna tym- ale podobny do synchronizacji czasu Wydaje mi się, że wersja mnie wyprowadza.

+0

Hm ... więc myślę, że pytanie jest więc logiczną, co jest poprawne * * sposób to zrobić? – Mehrdad

+0

Edytowane, aby dodać, jak myślę, że powinno działać. Ponownie, może być całkowicie błędne. –

+0

Dzięki! Chociaż myślałem, że celem "czekania" jest zmiana pozostałej metody na kontynuację stylu (a cały punkt asynchronicznego CTP polega na wyeliminowaniu potrzeby tak dużej instalacji hydraulicznej z delegatami/lambdami) ... więc jak to różni się od np 'BeginInvoke' i co tam, co mamy już od co najmniej .NET 2.0? – Mehrdad