Click here to Skip to main content
15,867,330 members
Articles / Programming Languages / C#
Article

OpenMP style multi-threading in C#

Rate me:
Please Sign up or sign in to vote.
4.27/5 (11 votes)
17 Sep 2008CPOL4 min read 73.3K   1K   22   14
Simplified multi-threading of for loops in C#, with an interface akin to OpenMP.

Introduction

This article demonstrates the use of C# anonymous delegates and a using() statement to achieve a very simple interface to multi-threading, in a manner in akin to OpenMP in C++.

It also provides an implementation and example of a multi-threading object, avForThread.

Background

Threading, in any language, introduces problems at all levels, from design, to implementation, to debugging. The OpenMP extension to Visual C++ allows programmers either to quickly parallelise otherwise sequential functions, or at least to test whether more specialised threading routines will be beneficial for their task. Unfortunately, currently, C# has no OpenMP extension implementation (at the time of writing, at least!)

However, C# does have several very elegant toys which can be combined to produce a coding interface very similar to that of OpenMP; primarily anonymous delegates, and using().

Anonymous delegates sport two great properties. One, they can be casually defined within function definitions; two, objects defined outside of the scope of the delegate, but within the defining scope, are still visible and accessible.

For example:

C#
public delegate void testSetter(int setTo); 
void myMethod()
{
  int b;
  testSetter D = delegate(int setTo) { b= setTo; }

  D(123);
}

In this case, the delegate instance D is set to an anonymous delegate defined inline. The delegate, in this case, sets b to the value of the incoming parameter.

Whilst this syntax is evidently not useful in this case, it does allow the programmer a large amount of flexibility, especially when it comes to dynamic querying, or polymorphic behaviour without the need for the bloat of new classes.

One use where this syntax is very useful is in being able to wrap up functions with any number of parameters and have threads execute them.

For example, if we pass an anonymous delegate to the constructor of a System.Threading.Thread object, we can then run the delegate with the parameters of our choosing.

So, let's say we want to take a for loop and split it into a number of threads. (In fact, the provided code only handles for loops, but it could be easily extended to handle any sequential mutual calculations within any looping structure - just don't write to data which may be read or written to by another thread!) As we want to use the calculated data immediately, we will wait for the threads to return.

The code attached defines all tasks needed for this kind of operations within one class, avForThread.

To remove the need to call a method to wait for the completion of the threads, we can employ a trick with a using() statement such that the execution will not leave the using block until the threads have returned. This is achieved by the call to the thread wait method, avForThread.WaitToFinish(), stated within avForThread.Dispose().

Using the code

So, we will start by creating a test for loop, the kind of code that could be parallelised...

C#
static void Main(string[] args)
{
    // we want to call calculateSomething 1M times.
    int dataSize = 1000000;

    // we'll store the data in an array
    int[] data = new int[dataSize];

    // we'll do the same calculation twice,
    // once without thread, and another time with

    // without threading
    for (int i = 0; i < dataSize; i++)
    {
        data[i] = calculateSomething(i);
    }

We'll then move on to performing the same task using the threaded notation, using an avForThread object. We will need to pass the same start and end values used by our simple for loop. We will also pass the number of threads to use, and of course, the parameterised delegate to execute.

C#
     using (new avForThread(0, dataSize, 4,
        delegate(int start, int end)
        {
            for (int i = start; i < end; i++)
            {
                data[i] = calculateSomething(i);
            }
        })
    ) ;
}

N.B.: To change the looping range, we can modify the 0 or dataSize arguments. Only, we don't need to modify start or end - they will be automatically set per thread by avForThread.

As you can see, the syntax is very simple and flexible. You will need to test with differing numbers of threads to gain the best performance. Depending on the complexity of the task and the CPU, there will be a "sweet spot" where the threaded code is far faster than the non-threaded code. Experiment with numThreads in the range 2 to 20.

For your own implementations, you'll need to keep roughly the same format as the for loop has here. By the way, here, the using() simply forces the runtime to call the Dispose method of the class, which in turn will wait for the threads to finish.

You could also omit the using statement, carry on working, and then call the avForThread.WaitToFinish() method explicitly.

As I said, if you want to go beyond a simple i++ for loop, some simple customisation of avForThread will be required.

Have fun!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Other Lecturer, Researcher, Software Developer, Animator
United Kingdom United Kingdom
Adam Vanner has worked at the National Centre for Computer Animation at Bournemouth University in the UK since 2000.

Specialising in 3D animation software development, 3D animation productions and motion capture, he has worked on a wide range of projects from design and development of commercial animation software, to collaborating with artists on animation for artworks and theatre.

Comments and Discussions

 
QuestionGood Information Pin
Abhishek Nandy24-Oct-12 4:49
professionalAbhishek Nandy24-Oct-12 4:49 
GeneralMSDN implementation of this Pin
Gamingdrake13-Jun-11 16:10
Gamingdrake13-Jun-11 16:10 
Questionthread exit before the task complete Pin
mnansyah15-Sep-09 20:01
mnansyah15-Sep-09 20:01 
GeneralRestart thread Pin
Robin25-May-09 22:55
Robin25-May-09 22:55 
GeneralMore than one thread Issue (Alert!) Pin
Aram Azhari28-Jan-09 21:58
Aram Azhari28-Jan-09 21:58 
GeneralError in code Pin
mikhmv5-Dec-08 15:02
mikhmv5-Dec-08 15:02 
GeneralRe: Error in code Pin
umz15-May-09 5:32
umz15-May-09 5:32 
GeneralThread safety Pin
popart24-Sep-08 21:26
popart24-Sep-08 21:26 
GeneralRe: Thread safety Pin
adamvanner8-Oct-08 13:45
adamvanner8-Oct-08 13:45 
GeneralRe: Thread safety Pin
popart8-Oct-08 21:06
popart8-Oct-08 21:06 
GeneralThe sources contain the bug!!! Pin
qweqrsfeg6hdfm5wufd23-Sep-08 3:52
qweqrsfeg6hdfm5wufd23-Sep-08 3:52 
GeneralRe: The sources contain the bug!!! [modified] Pin
adamvanner8-Oct-08 13:35
adamvanner8-Oct-08 13:35 
QuestionMS Parallel Extensions library Pin
Nicholas Butler22-Sep-08 23:46
sitebuilderNicholas Butler22-Sep-08 23:46 
AnswerRe: MS Parallel Extensions library Pin
adamvanner8-Oct-08 13:38
adamvanner8-Oct-08 13:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.