Microsoft Thinks Different
I just saw the first Microsoft Windows ad featuring Bill Gates and Jerry Seinfeld. This ad campaign is Microsoft's answer to Apple's Mac vs. PC ads which have been going unchallenged for over two years.
Read more...
The thoughts of a mere mortal programmer
I just saw the first Microsoft Windows ad featuring Bill Gates and Jerry Seinfeld. This ad campaign is Microsoft's answer to Apple's Mac vs. PC ads which have been going unchallenged for over two years.
In this post I would like to continue from where I left of in my previous post about the Asynchronous Programming Model, and show how the APM could be more concise using Anonymous Methods and Lambda Expressions.
Using "classic" delegates the code to implement the APM looks like this (as shown in greater details in my previous post):
public void ClassicAsync(int num)
{
DateTime start = DateTime.Now;
Console.WriteLine("Starting async calculation at: {0}", start);
PrimeCalc calc = new PrimeCalc();
Func<int, int> del = calc.GetNextPrime;
del.BeginInvoke(num, CalcCompleted, start);
}
private void CalcCompleted(IAsyncResult result)
{
DateTime start = (DateTime)result.AsyncState;
Func<int, int> del = (Func<int, int>)((AsyncResult)result).AsyncDelegate;
int prime = del.EndInvoke(result);
DateTime end = DateTime.Now;
TimeSpan length = end.Subtract(start);
Console.WriteLine("Completed async calculation at: {0}", end);
Console.WriteLine("Async calculation took {0:F} seconds.", length.TotalSeconds);
}
public void AnonymousAsync(int num)
{
DateTime start = DateTime.Now;
Console.WriteLine("Starting anonymous async calculation at: {0}", start);
PrimeCalc calc = new PrimeCalc();
Func<int, int> del = calc.GetNextPrime;
del.BeginInvoke(num, delegate(IAsyncResult result)
{
int prime = del.EndInvoke(result);
Console.WriteLine(prime);
DateTime end = DateTime.Now;
TimeSpan length = end.Subtract(start);
Console.WriteLine("Completed anonymous async calculation at: {0}", end);
Console.WriteLine("Async calculation took {0:F} seconds.", length.TotalSeconds);
}, null);
}
public void LambdaAsync(int num)Read more...
{
DateTime start = DateTime.Now;
Console.WriteLine("Starting lambda async calculation at: {0}", start);
PrimeCalc calc = new PrimeCalc();
Func<int, int> del = calc.GetNextPrime;
del.BeginInvoke(num, result =>
{
int prime = del.EndInvoke(result);
Console.WriteLine(prime);
DateTime end = DateTime.Now;
TimeSpan length = end.Subtract(start);
Console.WriteLine("Completed lambda async calculation at: {0}", end);
Console.WriteLine("Async calculation took {0:F} seconds.", length.TotalSeconds);
}, null);
}
In my previous post I described how delegates have evolved over the various versions of the .NET framework. In this post I want to elaborate and describe how to use delegates to implement asynchronous programming.
Many of today's PCs have multiple processors/cores in them, and there's a definite trend among processor makers to gradually increase that number in future models. In order to utilize the increasing number of cores, computer programs need to parallelize their execution, and assign processor intensive tasks to dedicated threads. Writing robust multi-threaded software is not easy. Threads need to be synchronized, data locked, and dead-locks are difficult to avoid. This increases the challenge for developers who need to write robust and efficient multi-threaded code.
To help programmers out, Microsoft introduced the Asynchronous Programming Model which simplifies multi-threaded programming. They have implemented it themselves in many classes throughout the .NET framework such as the FileStream, Socket, WebRequest, SqlCommand, etc. All of these classes offer asynchronous method calls along-side the standard synchronous versions. The async method name always starts with BeginXxx, and offers a corresponding EndXxx. For example, the SqlCommand class offers an ExecuteReader method, plus an async version of the method - BeginExecuteReader. To comply with the APM it also offers an EndExecuteReader method.
All delegates implement this pattern out-of-the-box. The all offer the standard Invoke method to execute the target method, and also offer the BeginInvoke and EndInvoke methods to execute the target method asynchronously.
This is the main reasons I love delegates so much.
Let's get down to it. One of the complexities of multi-threaded programming is figuring out when an async process completed its work. This is where the APM offers a great deal of help. There are three techniques to find out when a BeginInvoke is done: wait, poll and callback. I'll use a sample to describe the three techniques.
class PrimeCalc
{
public int GetNextPrime(int num)
{
int p = num + 1;
while(true)
{
if(IsPrime(p))
{
break;
}
p++;
}
return p;
}
}
Func< as our delegate:PrimeCalc calc = new PrimeCalc();
Func<int, int> del = calc.GetNextPrime;
public void WaitUntilComplete(int num)
{
PrimeCalc calc = new PrimeCalc();
Func<int, int> del = calc.GetNextPrime;
IAsyncResult result = del.BeginInvoke(num, null, null);
// Do some other work here
// Suspend this thread until the async operation completes
int prime = del.EndInvoke(result);
}
public void Poll(int num)
{
PrimeCalc calc = new PrimeCalc();
Func<int, int> del = calc.GetNextPrime;
IAsyncResult result = del.BeginInvoke(num, null, null);
while (!result.IsCompleted)
{
// Do some other work here
}
// Get the result from the async operation
int prime = del.EndInvoke(result);
}
public void Callback(int num)
{
DateTime start = DateTime.Now;
Console.WriteLine("Starting async calculation at: {0}", start);
PrimeCalc calc = new PrimeCalc();
Func<int, int> del = calc.GetNextPrime;
del.BeginInvoke(num, CalcCompleted, start);
}
private void CalcCompleted(IAsyncResult result)
{
DateTime start = (DateTime)result.AsyncState;
Func<int, int> del = (Func<int, int>)((AsyncResult)result).AsyncDelegate;
int prime = del.EndInvoke(result);
DateTime end = DateTime.Now;
TimeSpan length = end.Subtract(start);
Console.WriteLine("Completed async calculation at: {0}", end);
Console.WriteLine("Async calculation took {0:F} seconds.", length.TotalSeconds);
}
private void CalcCompleted(IAsyncResult result)
Read more...
namespace AsyncProgrammingModel
{
class PrimeCalc
{
private List<int> primes;
public PrimeCalc()
{
primes = new ListList<int>();
primes.Add(2);
primes.Add(3);
}
public int GetNextPrime(int num)
{
int p = num + 1;
while (true)
{
// Create a list of all prime numbers up to p.
AddPrimesToList(p);
if (IsPrime(p))
{
break;
}
p++;
}
return p;
}
private void AddPrimesToList(int numberToTest)
{
int n = primes[primes.Count - 1] + 2;
while (n < numberToTest)
{
if (IsPrime(n))
{
primes.Add(n);
}
// Skip even numbers.
n += 2;
}
}
private bool IsPrime(int n)
{
bool foundDivisor = false;
bool exceedsSquareRoot = false;
int i = 0;
int divisor = 0;
// Stop the search if:
// there are no more primes in the list,
// there is a divisor of n in the list, or
// there is a prime that is larger than the square root of n.
while ((i < primes.Count) && !foundDivisor && !exceedsSquareRoot)
{
// The divisor variable will be the smallest
// prime number not yet tried.
divisor = primes[i++];
// Determine whether the divisor is greater than the square root of n.
if (divisor * divisor > n)
{
exceedsSquareRoot = true;
}
// Determine whether the divisor is a factor of n.
else if (n % divisor == 0)
{
foundDivisor = true;
}
}
return !foundDivisor;
}
}
}