Threading

A multithreaded program contains two or more parts that can run concurrently. Each part of such a program is called a thread, and each thread defines a separate path of execution. Thus, multithreading is a specialized form of multitasking. 


Multithreaded applications provide the illusion that numerous activities are happening at more or less the same time. But the reality is the CPU uses something known as “Time Slice” to switch between different threads.
The principal advantage of multithreading is that it enables you to write very efficient programs because it lets you utilize the idle time that is present in most programs. But using too many threads in our programs can actually “degrade” performance, as the CPU must switch between the active threads in the process which takes time. When a thread’s time slice is up, the existing thread is suspended to allow other thread to perform its business. For a thread to remember what was happening before it was kicked out of the way, it writes information to its local storage and it is also provided with a separate call stack, which again put extra load on the CPU.


Multithreading Fundamentals

There are two distinct types of multitasking: process-based and thread-based.
The differences between process-based and thread-based multitasking can be summarized like this:
Process-based multitasking handles the concurrent execution of programs, while Thread-based multitasking deals with the concurrent execution of pieces of the same program.
Process-based: Example — running word processor at the same time you are browsing the net.
Thread-based: Example — A text editor can be formatting text at the same time that it is printing.


The .NET Framework defines two types of threads: foreground and background.
By default when you create a thread, it is a foreground thread, but you can change it to a background thread. The only difference between a foreground and background thread is that a background thread will be automatically terminated when all foreground threads in its process have stopped.


The “Foregroundthreads have the ability to prevent the current application from terminating. The CLR will not shutdown an application until all foreground threads have ended. The “Backgroundthreads are viewed by the CLR as expandable paths of execution that can be ignored at any point of time even if they are laboring over some unit of work. Thus, if all foreground threads have terminated, any background threads operating are automatically killed when the application terminates.


All processes have at least one thread of execution, which is usually called the main thread because it is the one that is executed when your program begins. From the main thread you can create other threads.
The classes that support multithreaded programming are defined in the System.Threading namespace.
Thus, you will usually include this statement at the start of any multithreaded program:


Methods

  • Suspend() -> Suspends the execution of a thread till Resume() is called on that.
  • Resume() -> Resumes a suspended thread. Can throw exceptions for bad state of the thread.
  • Sleep() -> A thread can suspend itself by calling Sleep(). Takes parameter in form of milliseconds. We can use a special timeout 0 to terminate the current time slice and give other thread a chance to use CPU time
  • Join()-> Called on a thread makes other threads wait for it till it finishes its task.

States of a Thread

States of a thread can be checked using ThreadState enumerated property of the Thread object which contains a different value for different states.
  • Aborted -> Aborted already.
  • AbortRequested -> Responding to an Abort() request.
  • Background -> Running in background. Same as IsBackground property.
  • Running -> Running after another thread has called the start()
  • Stopped -> After finishing run() or Abort() stopped it.
  • Suspended -> Suspended after Suspend() is called.
  • Unstarted -> Created but start() has not been called.
  • WaitSleepJoin -> Sleep()/Wait() on itself and join() on another thread. If a thread Thread1 calls sleep() on itself and calls join() on the thread Thread2 then it enters WaitSleepJoin state. The thread exists in this state till the timeout expires or another thread invokes Interrupt() on it. 

Properties of a Thread

  • Thread.CurrentThread -> Static method gives the reference of the thread object which is executing the current code.
  • Name -> Read/Write Property used to get and set the name of a thread
  • ThreadState -> Property used to check the state of a thread.
  • Priority -> Property used to check for the priority level of a thread.
  • IsAlive -> Returns a Boolean value stating whether the thread is alive or not.
  • IsBackground -> Returns a Boolean value stating the running in background or foreground.

PriorityLevels of Thread

Priority levels of thread is set or checked by using an enumeration i.e. ThreadPriority. The valid values are for this enumeration are;
  • Highest
  • AboveNormal
  • Normal
  • BelowNormal
  • Lowest 

Synchronization in Threads

 Monitor

Mutex 

Thread Pool 

When we have multiple threads that share data, we need to provide synchronized access to the data. We have to deal with synchronization issues related to concurrent access to variables and objects accessible by multiple threads at the same time. This is controlled by giving one thread a chance to acquire a lock on the shared resource at a time. We can think it like a box where the object is available and only one thread can enter into and the other thread is waiting outside the box until the previous one comes out.
using System;
using System.Threading;
namespace CSharpThreadExample

{
    class Program
    {
        static void Main(string[] arg)
        {
            Console.WriteLine("*****Multiple Threads*****");
            Printer p=new Printer();
            Thread[] Threads=new Thread[3];
            for(int i=0;i<3;i++)
            {
                Threads[i]=new Thread(new ThreadStart(p.PrintNumbers));
                Threads[i].Name="Child "+i;
            }
            foreach(Thread t in Threads)
                t.Start();

            Console.ReadLine();
        }
    }
    class Printer
    {
        public void PrintNumbers()
        {
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(100);
                Console.Write(i + ",");
            }
            Console.WriteLine();
        }
    }
}
 
In the above example, we have created three threads in the main method and all the threads are trying to use the PrintNumbers() method of the same Printer object to print to the console. Here we get this type of output:
image003.jpg
Now we can see, as the thread scheduler is swapping threads in the background each thread is telling the Printer to print the numerical data. We are getting inconsistent output as the access of these threads to the Printer object is synchronized. There are various synchronization options which we can use in our programs to enable synchronization of the shared resource among multiple threads.

Using the Lock Keyword

In C# we use lock(object) to synchronize the shared object.
Syntax:
lock (objecttobelocked) { objecttobelocked.somemethod(); } Here objecttobelocked is the object reference which is used by more than one thread to call the method on that object. The lock keyword requires us to specify a token (an object reference) that must be acquired by a thread to enter within the lock scope. When we are attempting to lock down an instance level method, we can simply pass the reference to that instance. (We can use this keyword to lock the current object) Once the thread enters into a lock scope, the lock token (object reference) is inaccessible by other threads until the lock is released or the lock scope has exited.
If we want to lock down the code in a static method, we need to provide the System.Type of the respective class.

Converting the Code to Enable Synchronization using the Lock Keyword

public void PrintNumbers() { lock (this) { for (int i = 0; i < 5; i++) { Thread.Sleep(100); Console.Write(i + ","); } Console.WriteLine(); } }

OUTPUT

image004.jpg

Using the Monitor Type

The C# lock keyword is just a notation for using System.Threading.Monitor class type. The lock scope actually resolves to the Monitor class after being processed by the C# compiler.


Converting the Code to Enable Synchronization using the Monitor Class

public void PrintNumbers() { Monitor.Enter(this); try { for (int i = 0; i < 5; i++) { Thread.Sleep(100); Console.Write(i + ","); } Console.WriteLine(); } finally { Monitor.Exit(this); } } Monitor.Enter() method is the ultimate recipient of the thread token. We need to write all code of the lock scope inside a try block. The finally clause ensures that the thread token is released(using the Monitor.Exit() method), regardless of any runtime exception.

OUTPUT

image005.jpg
In this article I have tried to make the explanation as simple as possible. I have referred the book written by Andrew Troelson published by Apress. Please provide your valuable comments and suggestions for improvement of this article. I am always open for future upgradation of the article.


  synchronization can be accomplished using the C# lock keyword and the Pulse 
method  of the Monitor object. The Pulse method notifies a thread in the waiting
queue of a change in the object's state (for more details on pulses, see the  
Monitor.Pulse Method). 
 
Only the current owner of the lock can signal a waiting object using Pulse.
The thread that currently owns the lock on the specified object invokes this method to signal the next thread in line for the lock. Upon receiving the pulse, the waiting thread is moved to the ready queue. When the thread that invoked Pulse releases the lock, the next thread in the ready queue (which is not necessarily the thread that was pulsed) acquires the lock.
Note that a synchronized object holds several references, including a reference to the thread that currently holds the lock, a reference to the ready queue, which contains the threads that are ready to obtain the lock, and a reference to the waiting queue, which contains the threads that are waiting for notification of a change in the object's state. The Pulse, PulseAll, and Wait methods must be invoked from within a synchronized block of code. The remarks for Wait(Object, Int32) address an issue that arises when Pulse is invoked before Wait.
To signal multiple threads, use the PulseAll method.
 
 
The example creates a Cell object that has two methods: ReadFromCell and WriteToCell.
Two other objects are created from classes CellProd and CellCons; these objects both have a
method ThreadRun whose job is to call ReadFromCell and WriteToCell. Synchronization is 
accomplished by waiting for "pulses" from the Monitor
 object, which come in order. That is, first an item is produced (the 
consumer at this point is waiting for a pulse), then a pulse occurs, 
then the consumer consumes the production (while the producer is waiting
 for a pulse), and so on.