C#.NET Threaded Programming

          

C#.NET Threaded Programming 

Simplifying Parallelism Complexity

In this chapter, we will drastically simplify the creati on of new parallelized code avoiding some advanced concurrent programming diffi culti es. Reading this chapter and following the exercises we shall:

  • Learn to combine single-threaded code with multi threaded code
  • Use of object-oriented design patt erns to simplify the creati on of parallelized code
  • Solve various problems to specialize in segmentati on algorithms and achieve thread affi nity
  • Encapsulate multi threaded 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 

C#.NET Threaded Programming 
This chapter excerpt from C# 2008 and 2005 Threaded Programming: Beginner's Guide by Gastón C. Hillar, is printed with permission from Packt Publishing, Copyright 2007.  

Specializing the algorithms for segmentation with classes

So far, we have been developing applicati ons that split work into multi ple independent jobs and created classes to generalize the algorithms for segmentati on. We simplifi ed the creati on of segmented and parallelized algorithms, generalizing behaviors to simplify our code and to avoid repeati ng the same code on every new applicati on. However, we did not do that using inheritance, a very powerful object-oriented capability that simplifi es code re-use. C# is an object-oriented programming language that supports inheritance and off ers many possibiliti es to specialize behaviors to simplify our code and to avoid some synchronizati on problems related to parallel programming. How can we use C# object-oriented capabiliti es to defi ne specifi c 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 creati onal patt ern (also known as virtual constructor). Thus, we can advance into creati ng a complete framework to simplify the algorithm opti mizati on process. Again, we can combine multi threading with object-oriented capabiliti es to reduce our development ti me and avoid synchronizati on problems.

You made the necessary changes to the ParallelAlgorithmPiece and the ParallelAlgorithm classes to possibly fi nd planets similar to Mars in the images corresponding to diff erent galaxies.

NASA's CIO was impressed with your parallel programming capabiliti es. Nevertheless, he is an object-oriented guru, and he gave you the advice to apply the factory method patt ern 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 scienti sts would then bring you another huge image processing challenge for your parallel programming capabiliti es—a sunspot analyzer. If you resolve this problem using the factory method patt ern or something like that, he will hire you! However, be careful, because you must avoid some synchronizati on 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 patt ern to specialize in parallel algorithms:

  1. Create a new C# Project using the Windows Forms Applicati on template in Visual 1. Studio or Visual C# Express. Use SunspotsAnalyzer as the project's name.
  2. Open the code for 2. Program.cs.
  3. Replace the line 3. [STAThread] with the following line (before the Main method declarati on):
    [MTAThread]
  4. Copy the fi le that contains the original code of the 4. ParallelAlgorithmPiece and the ParallelAlgorithm classes (ParallelAlgorithm.cs) and include them in the project.
  5. Add the 5. abstract keyword before the declarati ons of the ParallelAlgorithmPiece 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 6. ThreadMethod method declarati on 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 7. the ParallelAlgorithm class (the key to the factory method patt ern):
    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 9. ParallelAlgorithmPiece class CreatePieces method and paste it in the ParallelAlgorithm class (we move it to allow creati on of parallel algorithm pieces of diff erent subclasses). Replace the lloPieces[i].priBegin and lloPieces[i].priEnd private variables' access with their corresponding public properti es access lloPieces[i].piBegin and lloPieces[i].piEnd.
  10. Change the new 10. CreatePieces method declarati on in the ParallelAlgorithm class (remove the stati c 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 11. lloPieces[i] = new ParallelAlgorithmPiece(); in the CreatePieces method declarati on in the ParallelAlgorithm class with the following line of code (now the creati on is encapsulated in a method, and also, a great bug is corrected, which we will explain later):
    lloPieces.Add(CreateParallelAlgorithmPiece(i));
  12. Toi di lang thang lan trong bong toi buot gia, ve dau khi da mat em roi? Ve dau khi bao nhieu mo mong gio da vo tan... Ve dau toi biet di ve dau? http://nhatquanglan1.0catch.com
    //lloPieces[i].piThreadNumber = i;
  13. Replace the line 13. prloPieces = ParallelAlgorithmPiece. CreatePieces(priTotalElements, priTotalParts); in the CreateThreads method declarati on in the ParallelAlgorithm class with the following line of code (now the creati on is done in the new CreatePieces method):
    prloPieces = CreatePieces(priTotalElements, priTotalParts);
  14. Change the 14. StartThreadsAsync method declarati on in the ParallelAlgorithm class (add the virtual keyword to allow us to override it in subclasses):
    public virtual void StartThreadsAsync()
  15. Change the 15. CollectResults method declarati on 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 variati on of the factory method class creati onal patt ern, 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 fi ll in the gaps and override some methods, and we can then focus on the algorithm problems instead of working hard on the splitti ng techniques.

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

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 creati onal patt ern, we solved this problem with great simplicity. We made the necessary changes to the ParallelAlgorithmPiece and ParallelAlgorithm classes to implement a variati on of this design patt ern.

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 additi onal initi alizati on 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 representati on. 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 intenti onal bug left in the previous CreatePieces method. As you must master lists and collecti ons management in C# in order to master parallel programming, you should be able to detect and solve this litt le 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 creati on is now encapsulated in this method, which is virtual, and allows subclasses to override it. The original implementati on 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.

We made additi onal changes needed to keep conceptual integrity with this new approach for the two classes that defi ne the behavior of a parallel algorithm that splits work into pieces using multi threading capabiliti es.

Preparing the classes for inheritance

Apart from implementi ng a variati on of the factory method design patt ern, 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' declarati ons 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 additi onal initi alizati on 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 additi onal initi alizati on code that is needed. Remember that constructors do not require the virtual keyword.
  • CreateParallelAlgorithmPiece This is used to return a new instance of a specifi c ParallelAlgorithmPiece subclass.
  • StartThreadsAsync  This is used to append any additi onal code needed before or aft er starti ng the threads with an asynchronous executi on.
  • 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 splitti ng capabiliti es, we must create the subclasses and fi ll in the gaps.

Now, you have just created two subclasses and programmed the UI management code in order to create any split job, multi threaded algorithm. The NASA scienti sts have a keen interest in analyzing sunspots. However, the colors off ered by the original, very high-resoluti on images make it diffi cult to generate the stream inputs necessary for the servers to make graphs about the sunspots' evoluti on.

They want you to help them in developing a simple applicati on that has to invert the colors of many of these huge images, because there is no soft ware 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 Opti cal Telescope on November 20, 2006:

Simplifying Parallelism Complexity

This image reveals the structure of the solar magneti c fi eld rising verti cally from a sunspot outward into the solar atmosphere. A sunspot is an area of strong magneti c field.

You have to work on a very fast and very effi cient image color inverter, capable of changing the pixel colors to their opposite in the huge image, to make them capable of streaming to the stati sti cs servers. NASA's CIO wants you to use a very fi ne-tuned multi threading applicati on based on subclasses to demonstrate your object-oriented capabiliti es 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 fi ll in the gaps, overriding the necessary methods to defi ne 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 declarati on:
    class SunspotAnalyzerPiece : ParallelAlgorithmPiece
  3. Add the following lines of code at the beginning (as we are including the classes in a new applicati on 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 defi ne 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.

Creating a complete piece of work

First, we added the variables and properti es needed for the piece of work. We will be working with a porti on of a bitmap. So, we defi ned the proBitmap private variable and its poBitmap property.

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

public SunspotAnalyzerPiece(int priThreadNumberToAssign)
        : base(priThreadNumberToAssign)

This declarati on calls the base constructor (ParallelAlgorithmPiece constructor) with the priThreadNumberToAssign received as a parameter, and allows us to add additi onal initi alizati on code if any. We are therefore overriding the constructi on, 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, casti ng 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 defi ne a loPiece local variable with the SunspotAnalyzerPiece type, in order to avoid having to cast to SunspotAnalyzerPiece many ti mes. The code is easier to understand this way.

Writing the code for a thread in an instance method

Developing multi threaded algorithms using stati c members is easy because most types of public stati c members are thread-safe. Nevertheless, writi ng real-life applicati ons based on this rule is impossible.

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

All public stati c (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 applicati ons. They create risks when we do not follow certain rules when programming multi threaded applicati ons. However, it is the only way to create real-life applicati ons and parallel algorithms that do something interesti ng—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-off s, 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 diff erence between this and the pixel color converted to an Int32 Alpha, Red, Green, Blue value. It is simple, pure mathemati cs.

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++)

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

Tra lai em niem vui khi duoc gan ben em, tra lai em loi yeu thuong em dem, tra lai em niem tin thang nam qua ta dap xay. Gio day chi la nhung ky niem buon... http://nhatquanglan1.0catch.com

  1. Stay in the project, SunspotsAnalyzer.
  2. Create a new class, SunspotAnalyzer (a subclass of ParallelAlgorithm), using the following declarati on:
    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 resulti ng 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 properti es 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 functi on, CropBitmap. It will crop the bitmap received as a parameter and return the portion of the original defi ned 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(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, properti es, 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 specifi c Bitmap pieces is now held in the new SunspotAnalyzer class (a subclass of ParallelAlgorithm). The instance of this class will consti tute a very simple-to-use algorithm. We must override everything we need to complete the methods, as the algorithm coordinati on resides in the instance of this class.

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

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, defi ned 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 specifi c 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 additi onal method calls.

We already know the CreateBitmapParts method and the CropBitmap functi on. 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 properti es for each piece defi ned, 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.

We have created the algorithm coordinati on subclass, and added the variables, properti es, and methods needed for the sunspot analyzer algorithm.

Now, we are going to fi ll in the gaps overriding the necessary methods to defi ne the code for creati ng and coordinati ng 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 collecti on before starti ng the threads with an asynchronous executi on:
    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 specifi c 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, creati ng an instance of the SunspotAnalyzer class, we can easily invert any bitmap using as many threads as the number of available cores. We have fi lled in the gaps and have overridden some methods focusing on the algorithm problems instead of working hard on splitti ng techniques.

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 starti ng the threads is a good practi ce. 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();

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 defi ned, 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;

Do not forget the NASA scienti sts and NASA's CIO. They are excited about your new applicati on, 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, fi rst, we are going to create a new method for processing an individual bitmap. This way, we will be able to call it many ti mes for each bitmap found in a folder:

  1. Stay in the project, SunspotsAnalyzer.
  2. Add the following procedure in the form's fi le, AnalyzeSunspotsAndShowResult. It will create a SunspotAnalyzer instance, run the algorithm, and show the resulti ng 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 (inverti ng the bitmap colors) and show the resulti ng bitmap in the picturebox control is now held in a procedure already prepared for creati ng 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 creati ng an instance and calling two methods. The magic of object-orientati on 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 multi ple threads, cores, processors, and so on. Everything is encapsulated in the parallel algorithm-specifi c subclass.

First, it creates a SunspotAnalyzer instance, passing the enti re bitmap received as a parameter to the constructor.
loSunSpotAnalyzer = new SunspotAnalyzer(proBitmap);

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

loSunSpotAnalyzer.RunInParallelSync();

The threads are executed asynchronously. However, the RunInParallelSync method does not return unti l all the threads are fi nished 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;

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:

    C#.NET Threaded Programming

    One picturebox (picSunspots) with its SizeMode property set to StretchImage.
    One butt on showing a space shutt le and its Text property set to Run sunspots analyzer batch (butRunBatch). This butt on will start an instance of the parallel algorithm subclass, calling the AnalyzeSunspotsAndShowResult method for each image fi le 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 butt on 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 specifi ed folder (the default is C:\\NASASUNSPOT). You will need more than 10 images with high resoluti ons to see the progress.
  6. Build and run the applicati on.
  7. Click on the Run sunspots analyzer batch butt on. The images will be shown with their colors inverted, one aft er the other, with a delay (depending on the parallel processing capabiliti es of the computer) of a few seconds, as shown in the following image:

    C#.NET Threaded Programming

What just happened?

The NASA scienti sts 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 resoluti on images. You do not need to keep your fi ngers 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 multi threading problems.

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

Optimizing and encapsulating parallel algorithms

Look at the code in the fi nal applicati on! Using an object-oriented generalizati on process, we dramati cally simplifi ed the parallel algorithm creati on process.

Now, we could create a parallelized bitmap processing algorithm by creati ng 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 opti mize the algorithm making some changes to the methods.

Achieving thread affinity

Using and improving the classes and the encapsulati on capabiliti es off ered by the C# programming language, and accompanied by a good design, we can achieve the concept known as thread or resource affi nity. We have been working on that without talking about it—someti mes you must run before you can walk!

Thread affi nity promotes a task-oriented programming. We worked hard on splitti ng 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 collecti ng states, but not changing them, except in the case of some very simple fl ags to interrupt the processing.

In real-life applicati ons, 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-ti me-consuming framework. The performance improvements we achieve with multi threading could be lost with the instructi ons required to implement a very effi cient affi nity.

There are many techniques required to achieve thread affi nity. Their usage depends on the kind of applicati on we are developing. If we are working on a new multi threaded kernel, on a complex middleware, or on a low-level service, we will have to be very careful about the states, the affi nity, and the problemati c synchronizati on directi ves and objects.

To ensure thread affi nity, we must not allow any foreign thread to make changes in the variables in which our threads are working, except some simple fl ags 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 capabiliti es to simplify the code and make it easier to understand. We have specialized classes using inheritance and many encapsulati on techniques to simplify the parallel algorithm creati on process. Hence, we avoided repeati ng the same code on every new applicati on.

However, we have been avoiding some common nightmares related to multi threaded programming– locks and synchronizati on problems. How can we use C# and .NET capabiliti es to synchronize access to variables shared by several diff erent threads?

The answer is very simple, avoiding locks and dodging synchronizati on 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.

The best way to understand synchronizati on problems in multi threaded applicati ons is to compare them with the concurrency problems found in databases used by many users at the same ti me.

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 diff erent situati ons. Someti mes, you must lock the register unti l you are fi nished with writi ng the changes. Why? Because many users may be reading and writi ng concurrently to those records by using a diff erent computer connected to the same database. The same happens with multi ple threads in modern multi processing capable computers; many threads can change values at the same ti me. This is indeed very dangerous. We must be very careful when working with databases used by many users.

There are many synchronizati on classes, methods, and structures off ered by C# and .NET. They allow us to block one or many threads unti l a certain conditi on 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 diffi cult-to-solve bugs related to the synchronizati on mechanism.

Summary

We have learned a lot in this chapter about using object-oriented capabiliti es off ered by the C# programming language, using design patt erns for simplifying the parallelism complexity, and avoiding synchronizati on pains. Specifi cally, we covered:

  • Using the factory method class creati onal patt ern to create classes prepared for inheritance, and hence simplifying the creati on of parallelized code
  • Designing effi cient and pragmati c object-oriented multi threaded code
  • Creati ng instances and calling some methods in single-threaded code to create encapsulated and well-managed multi threaded code
  • Encapsulati ng multi threaded algorithms in classes to simplify the development process and to allow a division of the development team in single-threaded and multi threaded code
  • Creati ng safer, independent pieces of work fi lling the gaps in inherited classes
  • Specializing segmentati on algorithms, while avoiding synchronizati on pains and achieving thread affinity

We also learned the principles of thread affi nity, and how to avoid the undesirable sideeff ects related to concurrent programming.

Also see

C# interview questions with answers 

Define assembly.
What is a Constructor?
What is a Destructor?
Define abstract class in C#.NET.
Define serialization.
C#.Net support multiple inheritance, comment.
Can private virtual methods be overridden in C#.NET?
Is is possible to force garbage collector to run?
What is the role of data provider?
Describe how a .Net application is compiled and executed.
What is an Event?
Define Delegate.
What is Assembly manifest?
What is GAC (global assembly cache)?

.NET crystal reports

How do we access crystal reports in .NET?
What are the various components in crystal reports?
What basic steps are needed to display a simple report in crystal?..........

.Net Framework 

This includes introduction of .Net framework, .Net framework architecture, role of assembly and GAC.

.NET Code Security

This includes explanation of code security, Principal object, declarative and imperative security, role-based security, code access security and code group.



Write your comment - Share Knowledge and Experience


 

Latest MCQs
» General awareness - Banking » ASP.NET » PL/SQL » Mechanical Engineering
» IAS Prelims GS » Java » Programming Language » Electrical Engineering
» English » C++ » Software Engineering » Electronic Engineering
» Quantitative Aptitude » Oracle » English » Finance
Home | About us | Sitemap | Contact us | We are hiring