Simplifying Parallelism Complexity in C#

Exclusive offer: get 50% off this eBook here
C# 2008 and 2005 Threaded Programming: Beginner's Guide

C# 2008 and 2005 Threaded Programming: Beginner's Guide — Save 50%

Exploit the power of multiple processors for faster, more responsive software.

£14.99    £7.50
by Gastón C. Hillar | January 2009 | Microsoft

In order to simplify parallelism complexities and to avoid many concurrency pains, we must use the object-oriented capabilities offered by the C# programming language and design patterns. In this article, we will drastically simplify the creation of new parallelized code avoiding some advanced concurrent programming difficulties. Reading this article by Gastón C. Hillar and following the exercises we shall :

  • Learn to combine single-threaded code with multithreaded code
  • Use of object-oriented design patterns to simplify the creation of parallelized code
  • Solve various problems to specialize in segmentation algorithms and achieve thread affinity
  • Encapsulate multithreaded algorithms to create high-performance and safer independent pieces
  • Learn to avoid problems with design instead of solving them using very difficult-to-apply algorithms

Specializing the algorithms for segmentation with classes

So far, we have been developing applications that split work into multiple independent jobs and created classes to generalize the algorithms for segmentation. We simplified the creation of segmented and parallelized algorithms, generalizing behaviors to simplify our code and to avoid repeating the same code on every new application. However, we did not do that using inheritance, a very powerful object-oriented capability that simplifies code re-use. C# is an object-oriented programming language that supports inheritance and offers many possibilities to specialize behaviors to simplify our code and to avoid some synchronization problems related to parallel programming. How can we use C# object-oriented capabilities to define specific segmented algorithms prepared for running each piece in an independent thread using ParallelAlgorithm and ParallelAlgorithmPiece as the base classes?

The answer is very simple—by using inheritance and the factory method class creational pattern (also known as virtual constructor). Thus, we can advance into creating a complete framework to simplify the algorithm optimization process. Again, we can combine multithreading with object-oriented capabilities to reduce our development time and avoid synchronization problems.

Besides, using classes to specialize the process of splitting a linear algorithm into many pieces will make it easier for the developers to focus on generating very independent parts that will work well in a multithreading environment, while avoiding side-effects.

Time for action – Preparing the parallel algorithm classes for the factory method

You made the necessary changes to the ParallelAlgorithmPiece and the ParallelAlgorithm classes to possibly find planets similar to Mars in the images corresponding to different galaxies.

NASA's CIO was impressed with your parallel programming capabilities. Nevertheless, he is an object-oriented guru, and he gave you the advice to apply the factory method pattern to specialize the parallel algorithm classes in each new algorithm. That could make the code simpler, more re-usable, and easier to maintain.

He asked you to do so. The NASA scientists would then bring you another huge image processing challenge for your parallel programming capabilities—a sunspot analyzer. If you resolve this problem using the factory method pattern or something like that, he will hire you! However, be careful, because you must avoid some synchronization problems!

First, we are going to create a new project with tailored versions of the ParallelAlgorithmPiece and ParallelAlgorithm classes. This way, later, we will be able to inherit from these classes and apply the factory method pattern to specialize in parallel algorithms:

  1. Create a new C# Project using the Windows Forms Application template in Visual Studio or Visual C# Express. Use SunspotsAnalyzer as the project's name.
  2. Open the code for Program.cs.
  3. Replace the line [STAThread] with the following line (before the Main method declaration):
    [MTAThread]
  4. Copy the file that contains the original code of the ParallelAlgorithmPiece and the ParallelAlgorithm classes (ParallelAlgorithm.cs) and include them in the project.
  5. Add the abstract keyword before the declarations of theParallelAlgorithmPiece and the ParallelAlgorithm classes, as shown in the following lines (we do not want to create instances directly from these abstract classes):
    abstract class ParallelAlgorithmPiece
    abstract class ParallelAlgorithm
  6. Change the ThreadMethod method declaration in the ParallelAlgorithmPiece class (add the abstract keyword to force us to override it in subclasses):
    public abstract void ThreadMethod(object poThreadParameter);
  7. Add the following public abstract method to create each parallel algorithm piece in the ParallelAlgorithm class (the key to the factory method pattern):
    public abstract ParallelAlgorithmPiece
    CreateParallelAlgorithmPiece(int priThreadNumber);
  8. Add the following constructor with a parameter to the ParallelAlgorithmPiece class:
    public ParallelAlgorithmPiece(int priThreadNumberToAssign)
    {
    priThreadNumber = priThreadNumberToAssign;
    }
  9. Copy the original code of the ParallelAlgorithmPiece class CreatePieces method and paste it in the ParallelAlgorithm class (we move it to allow creation of parallel algorithm pieces of different subclasses). Replace the lloPieces[i].priBegin and lloPieces[i].priEnd private variables' access with their corresponding public properties access lloPieces[i].piBegin and lloPieces[i].piEnd.
  10. Change the new CreatePieces method declaration in the ParallelAlgorithm class (remove the static clause and add the virtual keyword to allow us to override it in subclasses and to access instance variables):
    public virtual List<ParallelAlgorithmPiece>
    CreatePieces(long priTotalElements, int priTotalParts)
  11. Replace the line lloPieces[i] = new ParallelAlgorithmPiece(); in the CreatePieces method declaration in the ParallelAlgorithm class with the following line of code (now the creation is encapsulated in a method, and also, a great bug is corrected, which we will explain later):
    lloPieces.Add(CreateParallelAlgorithmPiece(i));
  12. Comment the following line of code in the CreatePieces method in the ParallelAlgorithm class (now the new ParallelAlgorithmPiece constructor assigns the value to piThreadNumber):
    //lloPieces[i].piThreadNumber = i;
  13. Replace the line prloPieces = ParallelAlgorithmPiece. CreatePieces(priTotalElements, priTotalParts); in the CreateThreads method declaration in the ParallelAlgorithm class with the following line of code (now the creation is done in the new CreatePieces method):
    prloPieces = CreatePieces(priTotalElements, priTotalParts);
  14. Change the StartThreadsAsync method declaration in the ParallelAlgorithm class (add the virtual keyword to allow us to override it in subclasses):
    public virtual void StartThreadsAsync()
  15. Change the CollectResults method declaration in the ParallelAlgorithm class (add the abstract keyword to force us to override it in subclasses):
    public abstract void CollectResults();

What just happened?

The code required to create subclasses to implement algorithms, following a variation of the factory method class creational pattern, is now held in the ParallelAlgorithmPiece and ParallelAlgorithm classes.

Thus, when we create new classes that will inherit from these two classes, we can easily implement a parallel algorithm. We must just fill in the gaps and override some methods, and we can then focus on the algorithm problems instead of working hard on the splitting techniques.

We also solved some bugs related to the previous versions of these classes.

Using C# programming language's excellent object-oriented capabilities, we can avoid many problems related to concurrency and simplify the development process using high-performance parallel algorithms. Nevertheless, we must master many object-oriented design patterns to help us in reducing the complexity added by multithreading and concurrency.

Defining the class to instantiate

One of the main problems that arise when generalizing an algorithm is that the generalized code needed to coordinate the parallel algorithm must create instances of the subclasses that represent the pieces.

Using the concepts introduced by the factory method class creational pattern, we solved this problem with great simplicity. We made the necessary changes to the ParallelAlgorithmPiece and ParallelAlgorithm classes to implement a variation of this design pattern.

First, we added a constructor to the ParallelAlgorithmPiece class with the thread or piece number as a parameter. The constructor assigns the received value to the priThreadNumber private variable, accessed by the piThreadNumber property:

public ParallelAlgorithmPiece(int priThreadNumberToAssign)
{
priThreadNumber = priThreadNumberToAssign;
}

The subclasses will be able to override this constructor to add any additional initialization code.

We had to move the CreatePieces method from the ParallelAlgorithmPiece class to the ParallelAlgorithm class. We did this because each ParallelAlgorithm subclass will know which ParallelAlgorithmPiece subclass to create for each piece representation. Thus, we also made the method virtual, to allow it to be overridden in subclasses. Besides, now it is an instance method and not a static one.

There was an intentional bug left in the previous CreatePieces method. As you must master lists and collections management in C# in order to master parallel programming, you should be able to detect and solve this little problem. The method assigned the capacity, but did not add elements to the list. Hence, we must use the add method using the result of the new CreateParallelAlgorithmPiece method.

lloPieces.Add(CreateParallelAlgorithmPiece(i));

The creation is now encapsulated in this method, which is virtual, and allows subclasses to override it. The original implementation is shown in the following lines:

public virtual ParallelAlgorithmPiece CreateParallelAlgorithmPiece
(int priThreadNumber)
{
return (new ParallelAlgorithmPiece(priThreadNumber));
}

It returns a new ParallelAlgorithmPiece instance, sending the thread or piece number as a parameter.

Overriding this method, we can return instances of any subclass of ParallelAlgorithmPiece. Thus, we let the ParallelAlgorithm subclasses decide which class to instantiate.

This is the principle of the factory method design pattern. It lets a class defer instantiation to subclasses. Hence, each new implementation of a parallel algorithm will have its new ParallelAlgorithm and ParallelAlgorithmPiece subclasses.

We made additional changes needed to keep conceptual integrity with this new approach for the two classes that define the behavior of a parallel algorithm that splits work into pieces using multithreading capabilities.

C# 2008 and 2005 Threaded Programming: Beginner's Guide Exploit the power of multiple processors for faster, more responsive software.
Published: January 2009
eBook Price: £14.99
Book Price: £24.99
See more
Select your format and quantity:

Preparing the classes for inheritance

Apart from implementing a variation of the factory method design pattern, we had to prepare the classes for inheritance. We must override methods in order to create specialized classes. Therefore, we had to change some methods' declarations to make them virtual.

We must override the following methods in a ParallelAlgorithmPiece subclass:

  • The constructor: This is used to call the base constructor and append any additional initialization code that is needed. Remember that constructors do not require the virtual keyword.
  • ThreadMethod: This is the code that is going to be run by the thread.

We must override the following methods in a ParallelAlgorithm subclass:

  • The constructor: This is used to call the base constructor and append any additional initialization code that is needed. Remember that constructors do not require the virtual keyword.
  • CreateParallelAlgorithmPiece: This is used to return a new instance of a specific ParallelAlgorithmPiece subclass.
  • StartThreadsAsync: This is used to append any additional code needed before or after starting the threads with an asynchronous execution.
  • CollectResults: This is used to collect the results left in each ParallelAlgorithmPiece subclass instance.

Once the classes are prepared for a common parallel algorithm with job splitting capabilities, we must create the subclasses and fill in the gaps.

In order to simplify parallel programming, it is very important to master inheritance and design patterns as well as many other object-oriented code re-use techniques. Simpler code is easier to maintain. C# is a very powerful language, and you can use that power to simplify multithreaded applications.

Time for action – Creating a specialized parallel algorithm piece subclass

Now, you have just created two subclasses and programmed the UI management code in order to create any split job, multithreaded algorithm. The NASA scientists have a keen interest in analyzing sunspots. However, the colors offered by the original, very high-resolution images make it difficult to generate the stream inputs necessary for the servers to make graphs about the sunspots' evolution.

They want you to help them in developing a simple application that has to invert the colors of many of these huge images, because there is no software that is able to work with this huge number of pixels. In the following image, you can see an image taken by Hinode's Solar Optical Telescope on November 20, 2006:

Simplifying Parallelism Complexity in C#

This image reveals the structure of the solar magnetic field rising vertically from a sunspot outward into the solar atmosphere. A sunspot is an area of strong magnetic field.

You have to work on a very fast and very efficient image color inverter, capable of changing the pixel colors to their opposite in the huge image, to make them capable of streaming to the statistics servers. NASA's CIO wants you to use a very fine-tuned multithreading application based on subclasses to demonstrate your object-oriented capabilities combined with parallel programming knowledge. Of course, the algorithm must be capable of working with as many threads as the number of cores available in the computer in which the sunspot analyzer algorithm is being executed.

First, we are going to create a subclass of the ParallelAlgorithmPiece class and fill in the gaps, overriding the necessary methods to define the code to be executed for each piece. Thus, we are going to represent each piece of the sunspot analyzer parallel algorithm:

  1. Stay in the project, SunspotsAnalyzer.
  2. Create a new class, SunspotAnalyzerPiece (a subclass of ParallelAlgorithmPiece) using the following declaration:
    class SunspotAnalyzerPiece : ParallelAlgorithmPiece
  3. Add the following lines of code at the beginning (as we are including the classes in a new application that does not know anything about drawing, we are going to use the System.Drawing classes):
    using System.Drawing;
  4. Add the following private variable:
    // The resulting bitmap
    private Bitmap proBitmap;
  5. Add the following property to access the private variable (we want to create a compact and reliable class):
    public Bitmap poBitmap
    {
    set
    {
    proBitmap = value;
    }
    get
    {
    return proBitmap;
    }
    }
  6. Add a new constructor with a parameter that calls the base constructor:
    public SunspotAnalyzerPiece(int priThreadNumberToAssign)
    : base(priThreadNumberToAssign)
    {
    // Add any necessary additional instructions
    }
  7. Override the ThreadMethod method with the code that is going to be run by the thread:
    public override void ThreadMethod(object poThreadParameter)
    {
    // This is the code that is going to be run by the thread
    // It has access to all the instance variable of this class
    // It receives the instance as a parameter
    // We must typecast poThreadParameter to SunSpotAnalyzerPiece
    (inherited from ParallelAlgorithmPiece) to gain access to its members
    SunspotAnalyzerPiece loPiece;
    loPiece = (SunspotAnalyzerPiece)poThreadParameter;
    // Retrieve the thread number received in object
    poThreadParameter, in piThreadNumber property
    long liPieceNumber = loPiece.piThreadNumber;
    // The pixel matrix (bitmap) row number (Y)
    int liRow;
    // The pixel matrix (bitmap) col number (X)
    int liCol;
    // The pixel color
    Color loPixelColor;
    // Iterate through each pixel matrix (bitmap) row
    for (liRow = 0; liRow < proBitmap.Height; liRow++)
    {
    // Iterate through each pixel matrix (bitmap) col
    for (liCol = 0; liCol < proBitmap.Width; liCol++)
    {
    // Get the pixel Color for liCol and liRow
    loPixelColor = proBitmap.GetPixel(liCol, liRow);
    // Change the pixel color (invert the color)
    proBitmap.SetPixel(liCol, liRow, Color.FromArgb((
    Int32.MaxValue - loPixelColor.ToArgb())));
    }
    }
    }

What just happened?

The code to define the work done in the pieces of the algorithm is now held in the new SunspotAnalyzerPiece class (a subclass of ParallelAlgorithmPiece). The instances of this class are real independent parts of the sunspot analyzer algorithm. Everything we need to access from each thread for the algorithm resides in the instance of this class.

Look at the code. It is very simple to understand, compared to the code in our previous examples. We needed a bitmap and the code to invert it. That was exactly what we added. We forgot about splitting the work into many pieces, because that is done by the ParallelAlgorithmPiece and ParallelAlgorithm superclasses.

Now we have completed the skeleton for the piece of an algorithm defined as a subclass of the ParallelAlgorithmPiece class.

Creating a complete piece of work

First, we added the variables and properties needed for the piece of work. We will be working with a portion of a bitmap. So, we defined the proBitmap private variable and its poBitmap property.

Then, we defined a constructor with the same parameter used in the base class:

public SunspotAnalyzerPiece(int priThreadNumberToAssign)
: base(priThreadNumberToAssign)

This declaration calls the base constructor (ParallelAlgorithmPiece constructor) with the priThreadNumberToAssign received as a parameter, and allows us to add additional initialization code if any. We are therefore overriding the construction, but calling the base class constructor.

Then, we had to override the ThreadMethod method. This is the method that will run the code for each created thread (for each piece). We declared it with the same parameters as those in the base class:

public override void ThreadMethod(object poThreadParameter)

It receives the well-known object poThreadParameter parameter. However, it will be the corresponding instance of the SunspotAnalyzerPiece class assigned to that thread. Thus, casting the object poThreadParameter parameter to SunspotAnalyzerPiece, we will have a reference to the instance of SunspotAnalyzerPiece and hence, full access to the instance variables that are exclusive for the thread and completely independent of the others:

SunspotAnalyzerPiece loPiece;
loPiece = (SunspotAnalyzerPiece)poThreadParameter;

We define a loPiece local variable with the SunspotAnalyzerPiece type, in order to avoid having to cast to SunspotAnalyzerPiece many times. The code is easier to understand this way.

If we create other subclasses of the ParallelAlgorithmPiece, we must change the casting according to the subclass name in order to gain access to all its member variables and properties.

Writing the code for a thread in an instance method

Developing multithreaded algorithms using static members is easy because most types of public static members are thread-safe. Nevertheless, writing real-life applications based on this rule is impossible.

You will find this text in Microsoft Visual Studio helpful in many cases:

All public static (Shared in Visual Basic) members of this type are thread-safe. No instance member is guaranteed to be thread-safe.

We do need instance members for real-life applications. They create risks when we do not follow certain rules when programming multithreaded applications. However, it is the only way to create real-life applications and parallel algorithms that do something interesting—not just display numbers in the console output.

Hence, we write the code to be run in each thread in an instance method of the SunspotAnalyzerPiece class, which has access to those instance variables. It is a very independent block of code. Of course, it has some problems and trade-offs, but its main goal is to make it possible to split a huge task into many concurrent pieces.

Once the code retrieves its instance, it iterates through each pixel matrix (bitmap rows and columns) and inverts the colors in the following line of code:

proBitmap.SetPixel(liCol, liRow, Color.FromArgb((Int32.MaxValue -
loPixelColor.ToArgb())));

We take the maximum value of the Int32 data type to obtain the difference between this and the pixel color converted to an Int32 Alpha, Red, Green, Blue value. It is simple, pure mathematics.

As we are working in our piece of the bitmap, we begin the row number in 0, and take into account its Height property:

for (liRow = 0; liRow < proBitmap.Height; liRow++)

The possibility of accessing the instance corresponding to the thread makes it very easy to create highly independent blocks of code. Besides, it allows us to reduce the possibility of generating synchronization problems.

Time for action – Creating a specialized parallel algorithm coordination subclass

We have the pieces (instances of SunspotAnalyzerPiece), but now we must create the subclass to create and solve the sunspot analyzing algorithm.

We are going to create a subclass of the ParallelAlgorithm class and add the variables, properties, and methods needed for the sunspot analyzer algorithm. Thus, we are going to begin representing the sunspot analyzer parallel algorithm:

  1. Stay in the project, SunspotsAnalyzer.
  2. Create a new class, SunspotAnalyzer (a subclass of ParallelAlgorithm), using the following declaration:
    class SunspotAnalyzer : ParallelAlgorithm
  3. Add the following lines of code at the beginning, as we are going to use the System.Drawing classes):
    using System.Drawing;
  4. Add the following private variables (the original bitmap and the resulting one, the bitmaps list, and the total number of pieces or threads):
    // The Bitmap
    private Bitmap proOriginalBitmap;
    // The resulting bitmap
    private Bitmap proBitmap;
    // The bitmaps list
    private List<Bitmap> prloBitmapList;
    // The total number of pieces
    private int priTotalPieces;
  5. Add the following properties to access the private variables (we want to create a compact and reliable class):
    public Bitmap poBitmap
    {
    get
    {
    return proBitmap;
    }
    }
    public int piTotalPieces
    {
    get
    {
    return priTotalPieces;
    }
    }
  6. Add a constructor with a parameter (the bitmap to analyze or invert):
    public SunspotAnalyzer(Bitmap proBitmap)
    {
    proOriginalBitmap = proBitmap;
    // Create threads taking into account the number of lines in
    the bitmap and the number of available cores
    priTotalPieces = Environment.ProcessorCount;
    CreateThreads(proOriginalBitmap.Height, priTotalPieces);
    CreateBitmapParts();
    }
  7. Add the following function, CropBitmap. It will crop the bitmap received as a parameter and return the portion of the original defined by the Rectangle proRectangle:
    private Bitmap CropBitmap(Bitmap proBitmap, Rectangle
    proRectangle)
    {
    // Create a new bitmap copying the portion of the original
    defined by proRectangle and keeping its PixelFormat
    Bitmap loCroppedBitmap = proBitmap.Clone(proRectangle,
    proBitmap.PixelFormat);
    // Return the cropped bitmap
    return loCroppedBitmap;
    }
  8. Add the following procedure, CreateBitmapParts. It will assign the bitmap part corresponding to each piece:
    private void CreateBitmapParts()
    {
    // Create the bitmap list
    prloBitmapList = new List<Bitmap>(priTotalPieces);
    int liPieceNumber;
    Bitmap loBitmap;
    for (liPieceNumber = 0; liPieceNumber < priTotalPieces;
    liPieceNumber++)
    {
    loBitmap = CropBitmap(proOriginalBitmap, new Rectangle(0,
    (int) prloPieces[liPieceNumber].piBegin,
    proOriginalBitmap.Width, (int) (prloPieces
    [liPieceNumber].piEnd -
    prloPieces[liPieceNumber].piBegin + 1)));
    prloBitmapList.Add(loBitmap);
    // Assign the bitmap part corresponding to the piece
    ((SunspotAnalyzerPiece)prloPieces[liPieceNumber]).poBitmap
    = loBitmap;
    }
    }

What just happened?

We added the necessary variables, properties, and methods strictly related to a bitmap algorithm. We could have created a BitmapParallelAlgorithm class instead of directly working on the SunspotAnalyzer class.

The code to work with specific Bitmap pieces is now held in the new SunspotAnalyzer class (a subclass of ParallelAlgorithm). The instance of this class will constitute a very simple-to-use algorithm. We must override everything we need to complete the methods, as the algorithm coordination resides in the instance of this class.

Look at the new code. It is very simple to add the necessary things we need to the new subclass, because we can focus on the algorithm problems and forget about the multithreading complexity. The complex coordination work is done by the ParallelAlgorithm superclass.

Now we have completed the skeleton for the piece of an algorithm defined as a subclass of the ParallelAlgorithmPiece class.

C# 2008 and 2005 Threaded Programming: Beginner's Guide Exploit the power of multiple processors for faster, more responsive software.
Published: January 2009
eBook Price: £14.99
Book Price: £24.99
See more
Select your format and quantity:

 

Creating simple constructors

We created a very simple constructor that receives a Bitmap as a parameter:

public SunspotAnalyzer(Bitmap proBitmap)

It saves the original bitmap in the proOriginalBitmap private variable and creates the threads taking into account the number of lines in that bitmap and the number of available cores. However, this is accomplished by calling the CreateThreads method, defined in the ParallelAlgorithm superclass, using these simple lines:

priTotalPieces = Environment.ProcessorCount;
CreateThreads(proOriginalBitmap.Height, priTotalPieces);

It also saves the total number of pieces in the priTotalPieces private variable because we will need them later.

Then, it calls the specific CreateBitmapParts method. Again, we could have created a BitmapParallelAlgorithm class, but we must simplify the example.

The constructor leaves everything prepared to start running the threads without any additional method calls.

We already know the CreateBitmapParts method and the CropBitmap function. They work the same way they did in the previous examples. However, in this case, the CreateBitmapParts method takes into account the piBegin and piEnd properties for each piece defined, as shown in the following lines of code in the loop:

loBitmap = CropBitmap(proOriginalBitmap, new Rectangle(0,
(int) prloPieces[liPieceNumber].piBegin, proOriginalBitmap.Width,
(int) (prloPieces[liPieceNumber].piEnd
- prloPieces[liPieceNumber].piBegin + 1)));
prloBitmapList.Add(loBitmap);
((SunspotAnalyzerPiece)prloPieces[liPieceNumber]).poBitmap =
loBitmap;

In the last line, it casts prloPieces[liPieceNumber] to SunspotAnalyzerPiece because it must access its poBitmap property, which is exclusive of the subclass. It assigns the bitmap part corresponding to that piece.

These classes also help us to make a paradigm shift and begin thinking about the algorithms as pieces and a coordination work.

Time for action—Overriding methods in the coordination subclass

We have created the algorithm coordination subclass, and added the variables, properties, and methods needed for the sunspot analyzer algorithm.

Now, we are going to fill in the gaps overriding the necessary methods to define the code for creating and coordinating the pieces. Thus, we are going to represent the complete sunspot analyzer parallel algorithm:

  1. Stay in the project, SunspotsAnalyzer.
  2. Move to the SunspotAnalyzer : ParallelAlgorithm class code area.
  3. Override the StartThreadsAsync method to force a garbage collection before starting the threads with an asynchronous execution:
    public override void StartThreadsAsync()
    {
    // Call the garbage collector before starting each thread
    ForceGarbageCollection();
    // Run the base code
    base.StartThreadsAsync();
    }
  4. Override the CreateParallelAlgorithmPiece method with the code that is going to create the specific piece instance:
    public override ParallelAlgorithmPiece
    CreateParallelAlgorithmPiece(int priThreadNumber)
    {
    return (new SunspotAnalyzerPiece(priThreadNumber));
    }
  5. Override the CollectResults method with the code that is going to join the pieces (in this case, the bitmaps):
    public override void CollectResults()
    {
    // Enter the results collection iteration through the results
    left in each ParallelAlgorithmPiece
    int liPieceNumber;
    // Each bitmap portion
    Bitmap loBitmap;
    // Create a new bitmap with the whole width and height
    loBitmap = new Bitmap(proOriginalBitmap.Width,
    proOriginalBitmap.Height);
    Graphics g = Graphics.FromImage((Image)loBitmap);
    g.InterpolationMode = System.Drawing.Drawing2D.
    InterpolationMode.HighQualityBicubic;
    for (liPieceNumber = 0; liPieceNumber < priTotalPieces;
    liPieceNumber++)
    {
    // Draw each portion in its corresponding absolute
    starting row
    g.DrawImage(prloBitmapList[liPieceNumber], 0,
    prloPieces[liPieceNumber].piBegin);
    }
    // Assign the generated bitmap to proBitmap
    proBitmap = loBitmap;
    g.Dispose();
    }

What just happened?

The code required to implement the sunspot analyzer algorithm is now held in the SunspotAnalyzerPiece and SunspotAnalyzer classes.

Thus, creating an instance of the SunspotAnalyzer class, we can easily invert any bitmap using as many threads as the number of available cores. We have filled in the gaps and have overridden some methods focusing on the algorithm problems instead of working hard on splitting techniques.

Using C# object-oriented capabilities and some design patterns, we can design complete frameworks prepared for taking full advantage of parallel processing capabilities, avoiding the most common concurrency problems without too much code overhead.

Look at the code in the two subclasses. It is very easy to understand, and is also very well encapsulated.

We added a call to the ForceGarbageCollection method in the overridden StartThreadsAsync procedure. As we learned in the previous cases, forcing the garbage collector before starting the threads is a good practice. Then, we called the base code, because the behavior is then the same as in the superclass. The following line of code does that:

base.StartThreadsAsync();

Programming the piece creation method

We had to override the CreateParallelAlgorithmPiece method. This method will create the appropriate instance of a ParallelAlgorithmPiece subclass:

public override ParallelAlgorithmPiece
CreateParallelAlgorithmPiece(int priThreadNumber)
{
return (new SunspotAnalyzerPiece(priThreadNumber));
}

It returns a new SunspotAnalyzerPiece instance, sending the thread or piece number as a parameter.

Overriding this method, we can return instances of the SunspotAnalyzerPiece class (a subclass of ParallelAlgorithmPiece).

Programming the results collection method

We also had to override the CollectResults method. This method will collect the results left in each SunspotAnalyzerPiece subclass instance.

We already know the mechanism, since it is the same that we used in the previous examples. However, in this case, the CollectResults method takes into account the piBegin property for each piece defined, as shown in the following lines of code in the loop:

for (liPieceNumber = 0; liPieceNumber < priTotalPieces;
liPieceNumber++)
{
g.DrawImage(prloBitmapList[liPieceNumber], 0,
prloPieces[liPieceNumber].piBegin);
}

Then, it assigns the generated bitmap to proBitmap, which can be accessed from the outside world by the poBitmap property:

proBitmap = loBitmap;

Time for action – Defining a new method to create an algorithm instance

Do not forget the NASA scientists and NASA's CIO. They are excited about your new application, and you do not want to disappoint them. You would rather work for the NASA and not the FBI or the hackers!

As they want to run the algorithm for a lot of images, first, we are going to create a new method for processing an individual bitmap. This way, we will be able to call it many times for each bitmap found in a folder:

  1. Stay in the project, SunspotsAnalyzer.
  2. Add the following procedure in the form's file, AnalyzeSunspotsAndShowResult. It will create a SunspotAnalyzer instance, run the algorithm, and show the resulting bitmap in a picturebox control. It receives a Bitmap as a parameter:
    private void AnalyzeSunspotsAndShowResult(Bitmap proBitmap)
    {
    SunspotAnalyzer loSunSpotAnalyzer;
    loSunSpotAnalyzer = new SunspotAnalyzer(proBitmap);
    loSunSpotAnalyzer.RunInParallelSync();
    loSunSpotAnalyzer.CollectResults();
    picSunspots.Image = loSunSpotAnalyzer.poBitmap;
    }

What just happened?

The code required to analyze sunspots (inverting the bitmap colors) and show the resulting bitmap in the picturebox control is now held in a procedure already prepared for creating as many threads as the number of available cores with just a few lines of code. A Bitmap is received as a parameter for this method.

Forgetting about threads

Look at the code! There is nothing about threads, just a few lines creating an instance and calling two methods. The magic of object-orientation allows us to simplify several threading issues and reduce the parallelism complexity. The developer who writes this code could be a classic C# user interface or events programmer. He or she does not have to worry about multiple threads, cores, processors, and so on. Everything is encapsulated in the parallel algorithm-specific subclass.

First, it creates a SunspotAnalyzer instance, passing the entire bitmap received as a parameter to the constructor.

loSunSpotAnalyzer = new SunspotAnalyzer(proBitmap);

This call creates the pieces and gets everything prepared for an asynchronous execution. However, the method wants to return when everything is finished. Therefore, it calls the RunInParallelSync method:

loSunSpotAnalyzer.RunInParallelSync();

The threads are executed asynchronously. However, the RunInParallelSync method does not return until all the threads are finished with their work.

Then, it collects the results, leaving the processed bitmap accessible in the poBitmap property. Hence, it assigns it to the picSunspots picturebox:

loSunSpotAnalyzer.CollectResults();
picSunspots.Image = loSunSpotAnalyzer.poBitmap;

Using these classes as an initial pattern, we can create parallelized code encapsulated in classes in lesser time than initially expected and avoid lots of problems.

Time for action – Running the Sunspot Analyzer in many concurrent independent pieces

Now, we are going to create the UI and write some code to use the SunspotAnalyzer class and its encapsulated power:

  1. Stay in the project, SunspotsAnalyzer.
  2. Open the Windows Form Form1 (frmSunspotsAnalyzer) in the form designer, add the following controls, and align them as shown in the image:

    Simplifying Parallelism Complexity in C#

    => One picturebox (picSunspots) with its SizeMode property set to StretchImage.
    => One button showing a space shuttle and its Text property set to Run sunspots analyzer batch (butRunBatch). This button will start an instance of the parallel algorithm subclass, calling the AnalyzeSunspotsAndShowResult method for each image file found in a folder.

  3. Add the following lines of code at the beginning (as we are going to use the System.IO.DirectoryInfo and System.IO.FileInfo classes):
    using System.IO;
  4. Open the Click event in the button butRunBatch, and enter the following code (replace C:NASASUNSPOT with your images folder path):
    DirectoryInfo loDirectory;
    FileInfo[] loImageFiles;
    Bitmap loBitmap;
    // Replace "C:NASASUNSPOT" with your images folder path
    loDirectory = new DirectoryInfo("C:NASASUNSPOT");
    // Get the JPG files stored in the path
    loImageFiles = loDirectory.GetFiles("*.JPG");
    // Process each JPG image file found
    foreach (FileInfo loNextImageFile in loImageFiles)
    {
    // Store the Bitmap to be processed in the proOriginalBitmap
    private variable
    loBitmap = new Bitmap(Image.FromFile(loNextImageFile.
    FullName));
    AnalyzeSunspotsAndShowResult(loBitmap);
    // Let the PictureBox control update the image
    Application.DoEvents();
    }
  5. Copy the images to be analyzed in the specified folder (the default is C:NASASUNSPOT). You will need more than 10 images with high resolutions to see the progress.
  6. Build and run the application.
  7. Click on the Run sunspots analyzer batch button. The images will be shown with their colors inverted, one after the other, with a delay (depending on the parallel processing capabilities of the computer) of a few seconds, as shown in the following image:

    Simplifying Parallelism Complexity in C#

What just happened?

The NASA scientists and its CIO are very happy with your batch sunspot analyzer, and they are going to run it in the main server with 3,000 huge resolution images. You do not need to keep your fingers crossed, as the classes forced the garbage collector to run when needed. You are hired! However, you will need a more intensive training in avoiding some multithreading problems.

We could easily transform a new complex multithreaded algorithm capable of taking full advantage of parallel processing capabilities into subclasses of the generalized ParallelAlgorithmPiece and ParallelAlgorithm abstract superclasses. We can create instances of the subclass that represent the algorithm every time we need them, without worrying about threads and available cores thanks to object-oriented capabilities in the C# programming language.

Optimizing and encapsulating parallel algorithms

Look at the code in the final application! Using an object-oriented generalization process, we dramatically simplified the parallel algorithm creation process.

Now, we could create a parallelized bitmap processing algorithm by creating subclasses and making minor changes to the code.

Generalizing a parallel algorithm allows us to focus on the algorithm itself, and later we can easily optimize the algorithm making some changes to the methods.

Achieving a good encapsulation is one of the most important tasks to reduce the difficulties associated with multithreaded applications. Working with well-defined pieces that are as independent as possible is the key to success in the parallel programming world.

Achieving thread affinity

Using and improving the classes and the encapsulation capabilities offered by the C# programming language, and accompanied by a good design, we can achieve the concept known as thread or resource affinity. We have been working on that without talking about it—sometimes you must run before you can walk!

Thread affinity promotes a task-oriented programming. We worked hard on splitting a job into highly independent tasks. Each task works in an independent thread with its instance variables and its local variables. It does not change the states of variables visible to other threads.

We worked on asking states or collecting states, but not changing them, except in the case of some very simple flags to interrupt the processing.

In real-life applications, it is nearly impossible to hide all the state changes by one thread from the other threads. It is possible, but it requires developing a very complex and also a processing-time-consuming framework. The performance improvements we achieve with multithreading could be lost with the instructions required to implement a very efficient affinity.

There are many techniques required to achieve thread affinity. Their usage depends on the kind of application we are developing. If we are working on a new multithreaded kernel, on a complex middleware, or on a low-level service, we will have to be very careful about the states, the affinity, and the problematic synchronization directives and objects.

The best and the simplest way to achieve a successful thread affinity is coding a good design to avoid concurrency problems, taking into account the paradigm shift we have learned so far.

To ensure thread affinity, we must not allow any foreign thread to make changes in the variables in which our threads are working, except some simple flags to control its behavior.

Avoiding locks and many synchronization nightmares

So far, we have been working with parallelized algorithms and we've used C# object-oriented capabilities to simplify the code and make it easier to understand. We have specialized classes using inheritance and many encapsulation techniques to simplify the parallel algorithm creation process. Hence, we avoided repeating the same code on every new application.

However, we have been avoiding some common nightmares related to multithreaded programming–locks and synchronization problems. How can we use C# and .NET capabilities to synchronize access to variables shared by several different threads?

The answer is very simple, avoiding locks and dodging synchronization problems. The best way to simplify these common concurrency problems is to avoid them as much as possible, because they are always big nightmares for the developers.

Using many encapsulation techniques and some programming rules like the ones we have been working on, we can avoid locks and dodge synchronization problems.

The best way to understand synchronization problems in multithreaded applications is to compare them with the concurrency problems found in databases used by many users at the same time.

If you need to work on the same table's records (rows), you have to be very careful with the updates. There are many approaches, corresponding to many different situations. Sometimes, you must lock the register until you are finished with writing the changes. Why? Because many users may be reading and writing concurrently to those records by using a different computer connected to the same database. The same happens with multiple threads in modern multiprocessing capable computers; many threads can change values at the same time. This is indeed very dangerous. We must be very careful when working with databases used by many users.

There are many synchronization classes, methods, and structures offered by C# and .NET. They allow us to block one or many threads until a certain condition is reached in another thread. They provide locking mechanisms to avoid changing the value of a variable in many threads simultaneously. However, our approach is to avoid using them as much as possible. This way, we can keep our code simpler, and can avoid many difficult-to-solve bugs related to the synchronization mechanism.

Have a go hero – Avoiding side-effects

NASA's CIO is very happy with your work so far. However, he thinks you are very individualistic; and now that he has hired you, he wants to test your capabilities to work in a group of developers. OK, you are a multithreading guru, but they have been working in NASA for many years! So, be quiet!

He wants you to teach the other developers how to use your classes. However, he warns you that they are very curious about object-oriented structures. As many of them had worked in Quality Assurance, they try to find as many bugs as possible using the classes in unexpected ways.

Therefore, you must avoid side effects. The classes must be prepared for unexpected concurrency, and they must avoid unauthorized changes in their states. You must create a new version of the classes and the application to achieve a pure thread affinity.

Each piece must work in an independent thread, but no other thread is allowed to be able to change its properties. You can do that exposing only properties and verifying whether the current thread is the original thread that created the instance. You must previously save the original thread that created the instance, and then you can compare it with System.Threading.Thread.CurrentThread.

The developers will add buttons to change unexpected values in other threads, while your multithreaded algorithm is running. For example, they will want to change the SunspotAnalyzer instance, priTotalPieces. Don't let this happen!

Then, create new subclasses of the ParallelAlgorithmPiece and the ParallelAlgorithm classes to develop a new algorithm to parallelize statistics about the emails that the NASA info inbox receives.

Use a good object-oriented design and have thread affinity in mind while doing it. Show the number of paragraphs, the number of sentences, and the number of words as they are found in very huge texts. Do not create global counters; instead collect them as and when needed from the pieces to show partial results and a progress bar.

You can do it; we have been working with the classes and with good object-oriented designs.

Summary

We have learned a lot in this article about using object-oriented capabilities offered by the C# programming language, using design patterns for simplifying the parallelism complexity, and avoiding synchronization pains. Specifically, we covered:

  • Using the factory method class creational pattern to create classes prepared for inheritance, and hence simplifying the creation of parallelized code
  • Designing efficient and pragmatic object-oriented multithreaded code
  • Creating instances and calling some methods in single-threaded code to create encapsulated and well-managed multithreaded code
  • Encapsulating multithreaded algorithms in classes to simplify the development process and to allow a division of the development team in single-threaded and multithreaded code
  • Creating safer, independent pieces of work filling the gaps in inherited classes
  • Specializing segmentation algorithms, while avoiding synchronization pains and achieving thread affinity

We also learned the principles of thread affinity, and how to avoid the undesirable sideeffects related to concurrent programming.

About the Author :


Gastón C. Hillar

Gastón C. Hillar has been working with computers since he was eight. He began programming with the legendary Texas TI-99/4A and Commodore 64 home computers in the early 80s.
He has a Bachelor degree in Computer Science from which he graduated with honors, and an MBA from which he graduated with an outstanding thesis. Now, he is an independent IT consultant and a freelance author always looking for new adventures around the world.

To date he’s written more than 40 books in Spanish, and for Packt Publishing has written “C# 2008 and 2005 Threaded Programming: Beginner's Guide”. He usually writes articles for Spanish magazines Mundo Linux, Solo Programadores and Resistor.
He contributes to Dr. Dobb's Go Parallel programming portal http://www.ddj.com/go-parallel/ and he is a guest blogger at Intel Software Network http://software.intel.com

Gastón C. Hillar is the author of "Microsoft Silverlight 4 and SharePoint 2010 Integration".

Books From Packt

Programming Microsoft® Dynamics™ NAV
Programming Microsoft® Dynamics™ NAV

Quality Assurance for Dynamics AX-Based ERP Solutions
Quality Assurance for Dynamics AX-Based ERP Solutions

Implementing Microsoft Dynamics NAV 2009
Implementing Microsoft Dynamics NAV 2009

ASP.NET 3.5 Social Networking
ASP.NET 3.5 Social Networking

Software Testing with Visual Studio Team System 2008
Software Testing with Visual Studio Team System 2008

ASP.NET 3.5 Application Architecture and Design
ASP.NET 3.5 Application Architecture and Design

Active Directory Disaster Recovery
Active Directory Disaster Recovery

Entity Framework Tutorial
Entity Framework Tutorial

 

 

 

 

Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software