Async CTPのパフォーマンスが低いのはなぜですか?
-
28-10-2019 - |
質問
理由は本当にわかりません await
と async
ここで私のコードのパフォーマンスを改善しないでください 彼らがそうすることになっているように.
懐疑的ですが、私はコンパイラが私の方法を書き換えて、ダウンロードが並行して行われるようにすることになっていると思っていました...しかし、それは実際には起こっていないようです。
(私 行う 気付く await
と async
個別のスレッドを作成しないでください。ただし、OSはParallalでダウンロードを実行し、元のスレッドで私のコードを呼び戻す必要があります。)
私は使用していますか async
と await
不適切ですか?それらを使用する適切な方法は何ですか?
コード:
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??
解決
クリスの答えはほぼ正しいですが、人種条件を導入し、すべてのタスクで同期してブロックします。
一般的なルールとして、あなたが持っている場合、タスクの継続を使用しないことが最善です await
/async
利用可能。また、使用しないでください WaitAny
/ WaitAll
- async
同等のものです WhenAny
と WhenAll
.
私はこのようにそれを書きます:
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);
}
他のヒント
私はあなたのコードを誤解しているかもしれませんが、それはあなたが非同期の読み取りを行うためにバックグラウンドスレッドを起動していて、すぐにブロックして、それが完了するのを待っているようです。コードの「非同期」の部分については、実際には非同期ではありません。これを試して:
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);
}
私は間違っているかもしれません - 非同期のものは新しいものであり、私はそれに100%精通していません - しかし、同期バージョンと同様のタイミングは私を抱きしめているようです。