I don't really understand why await
and async
don't improve the performance of my code here like they're supposed to.
Though skeptical, I thought the compiler was supposed to rewrite my method so that the downloads were done in parallel... but it seems like that's not actually happening.
(I do realize that await
and async
do not create separate threads; however, the OS should be doing the downloads in parallal, and calling back my code in the original thread -- shouldn't it?)
Am I using async
and await
improperly? What is the proper way to use them?
Code:
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);
}
}
}
Output:
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??
I may be misreading your code, but it looks like you are launching a background thread to do the async reading and then immediately blocking, waiting for it to complete. Nothing about the 'async' portion of your code is actually async. Try this:
And use it thus:
I could be wrong- the async stuff is new and I'm not 100% familiar with it- but the similar timing to the sync version seems to bear me out.
Chris' answer is almost correct, but introduces a race condition and synchronously blocks on all the tasks.
As a general rule, it's best to not use task continuations if you have
await
/async
available. Also, do not useWaitAny
/WaitAll
- theasync
equivalents areWhenAny
andWhenAll
.I would write it like this: