Having researched the concept of asynchronous web development, specifically from this source, I created a sample application to prove the concept.
The solution is compos
I think there's a pretty good chance you're not testing what you think you're testing. From what I can gather, you're trying to detect releases back to the thread pool by comparing timings and deducing thread injection.
For one thing, the default settings for the thread pool on .NET 4.5 are extremely high. You're not going to hit them with just 10 or 100 simultaneous requests.
Step back for a second and think of what you want to test: does an async method return its thread to the thread pool?
I have a demo that I show to demonstrate this. I didn't want to create a heavy load test for my demo (running on my presentation laptop), so I pulled a little trick: I artificially restrict the thread pool to a more reasonable value.
Once you do that, your test is quite simple: perform that many simultaneous connections, and then perform that many plus one. The synchronous implementation will have to wait for one to complete before starting the last one, while the asynchronous implementation will be able to start them all.
On the server side, first restrict the thread pool threads to the number of processors in the system:
protected void Application_Start()
{
int workerThreads, ioThreads;
ThreadPool.GetMaxThreads(out workerThreads, out ioThreads);
ThreadPool.SetMaxThreads(Environment.ProcessorCount, ioThreads);
...
}
Then do the synchronous and asynchronous implementations:
public class ValuesController : ApiController
{
// Synchronous
public IEnumerable<string> Get()
{
Thread.Sleep(1000);
return new string[] { "value1", "value2" };
}
// Asynchronous
public async Task<IEnumerable<string>> Get(int id)
{
await Task.Delay(1000);
return new string[] { "value1", "value2" };
}
}
And finally the client testing code:
static void Main(string[] args)
{
try
{
MainAsync().Wait();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.ReadKey();
}
static async Task MainAsync()
{
ServicePointManager.DefaultConnectionLimit = int.MaxValue;
var sw = new Stopwatch();
var client = new HttpClient();
var connections = Environment.ProcessorCount;
var url = "http://localhost:35697/api/values/";
await client.GetStringAsync(url); // warmup
sw.Start();
await Task.WhenAll(Enumerable.Range(0, connections).Select(i => client.GetStringAsync(url)));
sw.Stop();
Console.WriteLine("Synchronous time for " + connections + " connections: " + sw.Elapsed);
connections = Environment.ProcessorCount + 1;
await client.GetStringAsync(url); // warmup
sw.Restart();
await Task.WhenAll(Enumerable.Range(0, connections).Select(i => client.GetStringAsync(url)));
sw.Stop();
Console.WriteLine("Synchronous time for " + connections + " connections: " + sw.Elapsed);
url += "13";
connections = Environment.ProcessorCount;
await client.GetStringAsync(url); // warmup
sw.Restart();
await Task.WhenAll(Enumerable.Range(0, connections).Select(i => client.GetStringAsync(url)));
sw.Stop();
Console.WriteLine("Asynchronous time for " + connections + " connections: " + sw.Elapsed);
connections = Environment.ProcessorCount + 1;
await client.GetStringAsync(url); // warmup
sw.Restart();
await Task.WhenAll(Enumerable.Range(0, connections).Select(i => client.GetStringAsync(url)));
sw.Stop();
Console.WriteLine("Asynchronous time for " + connections + " connections: " + sw.Elapsed);
}
On my (8-logical-core) machine, I see output like this:
Synchronous time for 8 connections: 00:00:01.0194025
Synchronous time for 9 connections: 00:00:02.0362007
Asynchronous time for 8 connections: 00:00:01.0413737
Asynchronous time for 9 connections: 00:00:01.0238674
Which clearly shows that the asynchronous method is returning its thread to the thread pool.