2012-01-15 17 views
6

awaitasyncのコードが私のコードの性能を向上させない理由がわかりません。like they're supposed to非同期CTPのパフォーマンスが低下しているのはなぜですか?

懐疑的ですが、私はコンパイラが自分のメソッドを書き換えてダウンロードを並行して行うことになっていると思っていましたが、実際には起こっていないようです。
私はがawaitasyncは別のスレッドを作成しないことを実現します;?しかし、OSはparallalでダウンロードをして、そして元のスレッドで私のコードをコールバックする必要がある - それはいけない

asyncawaitを不適切に使用していますか?それらを使用する適切な方法は何ですか?

コード:

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

出力:

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

答えて

10

Chrisの答えはほぼ正しいですが、競合状態が発生し、すべてのタスクが同期してブロックされます。

await/asyncが利用可能な場合は、一般的に、タスクの継続を使用しないことをお勧めします。また、WaitAny/WaitAllを使用しないでください。async相当品はWhenAnyWhenAllです。

私はこのようにそれを記述します...

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

ああ、これはもっときれいに見えます。 =)ありがとう! – Mehrdad

1

私はあなたのコードを誤読することができるが、あなたが待っている、非同期の読み取りを行うために、バックグラウンドスレッドを起動し、その後すぐにブロックされているように見えますそれが完了するために。あなたのコードの「非同期」部分は、実際には非同期ではありません。

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

をこうしてそれを使用する:これを試してみてください

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

私は非同期のものが新しいwrong-ことができ、私はIT-が、同期と同様のタイミングで100%熟知していませんよバージョンは私を耐えているようだ。

+0

フムので、私は論理的な質問が続いており、それを行うには*正しい*方法何だと思いますか? – Mehrdad

+0

私はそれが動作すると思われる方法を追加するために編集しました。再び、完全に間違っている可能性があります。 –

+0

ありがとう!私が「待っている」の全体のポイントは、残りのメソッドを継承パッシングスタイルに変更することだと思っていましたが(非同期CTPの全体的なポイントは、代理人/ラムダの配管の必要性をなくすことです)これは、例えば、 'BeginInvoke'とそれ以外のものは、少なくとも.NET 2.0以来持っていましたか? – Mehrdad

関連する問題