Wednesday, September 30, 2009

Demo Parallel Extensions .NET 4.0

Yesterday I a gave a presentation/demonstration about the upcoming .NET 4.0 framework release. Unfortunately, there wasn't enough time to show the complete parallel demo. Here I show a couple of ways to display all integers between 0 and 100 that are dividable by 10.


class Program
{
// objects for classic parallel code
static object locker = new object();
static Queue<int> integers;
//with concurrent Queue, we don't need a locker
static ConcurrentQueue<int> concurrentIntegers;

static void Main(string[] args)
{
int steps = 100;
steps.WriteOnConsole(); //extension method to display an integer on the console
Classic(); //classic for loop, no parallel code
ClassicParallel(); //classic parallel invocation

New(steps); //parallel for loop
NewMoreLikeClassic(steps); //parallel the new way, but looks like classic parallel

Linq(steps); //old LINQ way
PLinq(steps); //new Parallel LINQ
}

private static void Classic(int steps = 100)
{
for (int i = 0; i < steps; i++)
{
if (i % 10 == 0)
{
i.WriteOnConsole();
}
}
}

private static void ClassicParallel(int steps = 100)
{
integers = new Queue<int>(steps);
// fill the queue with all integers
for (int i = 0; i < steps; i++)
{
integers.Enqueue(i);
}

int workerCount = 5; //arbitrary number for workercount.
Thread[] workers = new Thread[workerCount];
// Create and start a separate thread for each worker
for (int i = 0; i < workerCount; i++)
{
workers[i] = new Thread(Consume);//.Start();
}
foreach (Thread worker in workers)
{
worker.Start();
}
}

private static void Consume()
{
while (true)
{
int i;
lock (locker)
{
if (integers.Count == 0) return; // run until the queue is empty
i = integers.Dequeue();
}
if (i % 10 == 0)
{
i.WriteOnConsole();
}
}
}

private static void New(int steps)
{
ParallelLoopResult result = Parallel.For(0, steps, (i, loop) =>
{
if (i % 10 == 0)
{
i.WriteOnConsole();
}
//With the LoopState, we can break and terminate the processing of the loop
if (i == 50)
{
loop.Break();
}
}
);
Console.WriteLine("Completed: {0}, Breaked at iteration {1}",
result.IsCompleted,
result.LowestBreakIteration);
}

private static void NewMoreLikeClassic(int steps = 100)
{
concurrentIntegers = new ConcurrentQueue<int>(Enumerable.Range(0, steps));
Parallel.Invoke(() =>
{
ConsumeQueue();
}
);
}

private static void ConsumeQueue()
{
// note: there is no locking used here, because we use a ConcurrentQueue
int i;
bool success = concurrentIntegers.TryDequeue(out i);
if (success && (i % 10 == 0))
{
i.WriteOnConsole();
}
}

private static void Linq(int steps)
{
Enumerable.Range(0,steps).Where(i => i % 10 == 0)
.ToList<int>().ForEach(i=>i.WriteOnConsole());
}

private static void PLinq(int steps)
{
Enumerable.Range(0, steps).Where(i => i % 10 == 0).AsParallel()
.ToList<int>().ForEach(i => i.WriteOnConsole());
}
}

No comments: