Threading in .NET

author-image
CIOL Bureau
Updated On
New Update

Understanding Threading


A thread sometimes called, as lightweight process, is a basic unit of CPU utilization. It consists of a program counter (pointer to the next instruction), a register set and stack space. It shares with its peer thread its code section, data section and operating system resources such as open files and signals collectively known as task. A thread context switch still requires a register set switch but the no memory related work need to be done and it is less expensive compared to switches among the heavy weight processes.


Switching between user level threads can be done independently of the operating system and therefore very quickly. Thus blocking a thread and switching to another thread is a reasonable solution to the problem of how a server can handle many requests efficiently.


Threads can be in one of the several states:


  • Ready

  • Blocked

  • Running

  • Terminated

Like processes , threads share the CPU and only one thread at a time is active. A thread within a process executes sequentially and each thread has its own stack and program counter. Threads can create child threads and can block waiting system calls to complete; if one thread is blocked another thread can run. However threads are not independent of one another. Because all threads can access every address in the task, a thread read or writes over any other thread's stacks. This structure does not provide protection between threads.


When an application executes, a primary thread is created, and the application scope is based on this thread. An Application can create additional threads to perform additional tasks. An example of creating a primary thread is starting Microsoft Word. The Application execution starts the main thread. Within the Word Application, the background printing of a document would be an example of an additional thread being created to handle another task. While you are still interacting with the main thread, the system is carrying out your printing request. After the main application thread is killed, all other threads created as a result of that thread are also killed.


Creating Multithreaded applications


Threading is handled through the System.Threading namespace. The common members of the thread class are listed in the table below.


 


 


Common Thread class Members























































Member


Description


CurrentContext


Returns the current Context on which the thread is executing


CurrentThread


Returns a reference to the currently running Thread


ResetAbort


Resets an abort request


Sleep


Suspends a Current Thread for a specified amount of time.


ApartmentState


Gets or Sets the Apartment state of the Thread


IsAlive


Gets a value that a indicates whether the thread is been started and is not dead


IsBackground


Gets a value that a indicates whether the thread is a background thread.


Name


Gets or Sets the name of the Thread


Priority


Gets or Sets the thread priority


ThreadState


Gets the state of the thread


Abort


Raises the ThreadAbortException,which can end the thread


Interrupt


Interrupts a thread that is in the waitsleepjoin thread State


Join


Waits for a thread


Resume


Resumes a thread that has been suspended


Start


Begins the thread execution


Suspend


Suspends the thread

 


 


Creating New Threads


Creating a variable of the System.Threading.Thread type enables you to create a new thread to start working with. Because the concept of threading involves the independent execution of another task, the Thread constructor requires the address of a procedure that will do the work for the thread you are creating. The ThreadStart delegate is the only parameter the constructor needs to begin using the thread.


Code for creating new Threads.


using

System;


using

System.Threading;

public

class thrds

{


publicvoid Threader1()


{


for (int i = 0;i <1000;i++)


{






Console.WriteLine("Thread Name : " + Thread.CurrentThread.Name);






}


}


publicvoid Threader2()


{


for (int i = 0;i <1000;i++)


{






Console.WriteLine("Thread Name : " + Thread.CurrentThread.Name);






}


}


}


public

class ThreadTest


{


publicstaticint Main(string<> args)


{


thrds test = new thrds();


Thread t1 = new Thread(new ThreadStart(test.Threader1));


t1.Name = "Threader1";


t1.Start();


Thread t2 = new Thread(new ThreadStart(test.Threader2));


t2.Name = "Threader2";


t2.Start();


Console.ReadLine();


return 0;




}


}



Understanding Thread Priority


For a Thread Procedure to finish before another thread procedure begins you need to set the Priority property to correct ThreadPriority Enumeration to ensure that this thread has priority over any other thread


Ex:


Thread1.Priority = ThreadPriority.Highest


The Thread priority enumeration dictates how a given thread is scheduled based on other running threads. ThreadPriority can be any one of the following


  • AboveNormal

  • Belownormal

  • Highest

  • Lowest

  • Normal

The Algorithm that determines thread scheduling varies depending on the operating system on which the threads are running. By default when a thread is created it is given a priority of 2 , which is normal in the enumeration.


 


Thread States


When you create a new thread, you call the start() method. At this point, the operating system allocates time slices to the address of the procedure passed in the thread constructor. Though the thread might live for a very long time, it still passes in different states while other threads are being processed by the operating system. Besides Start, the most common thread states you will use are sleep and abort. By passing number of milliseconds to the sleep constructor, you are instructing the thread to give up the remainder of its time slice. Calling the abort method stops the execution of the thread.


Example code for Sleep and Abort


using

System;


using

System.Threading;

public

class thrds

{


publicvoid Threader1()


{


for (int i = 0;i <1000;i++)


{


if (i == 5 )


{


Thread.Sleep(500);


Console.WriteLine("Thread1 Sleeping");


}


}


}


publicvoid Threader2()


{


for (int i = 0;i <1000;i++)


{


if (i == 5)


{


Thread.Sleep(500);


Console.WriteLine("Thread2 Sleeping");


}


}


}


}


public

class ThreadTest

{


publicstaticint Main(string<> args)


{


thrds test = new thrds();


Thread t1 = new Thread(new ThreadStart(test.Threader1));



t1.Start();


Thread t2 = new Thread(new ThreadStart(test.Threader2));


t2.Priority = ThreadPriority.Highest;


t2.Start();


Console.ReadLine();


return 0;



}


}



The Priority property is set to the highest for the second thread. This means that no matter what, it executes before first thread starts. However, in the Threader 2 procedure you have the following if block

Advertisment

for

(int i = 0;i <1000;i++)

{


if (i == 5)


{


Thread.Sleep(500);


Console.WriteLine("Thread2 Sleeping");


}


}


This tells the t2 thread to sleep for 500 milliseconds, giving up its current time slice and allowing the t1 thread to begin. After both threads are complete, the abort method is called and the threads are killed.


The Thread.Suspend method call, suspends a thread, indefinitely until another thread wakes it back up. To get the thread back on track, you need to call the resume method from another thread so it can restart itself. The following code demonstrates Suspend and Resume method:


Thread.CurrentThread.Suspend;


Console.Writeline("Thread1 Suspended");


Thread.CurrentThread.Resume;


Console.Writeline("Thread1 Resumed");


Thread State is a bitwise combination of the Flags Attribute enumeration. At any given time, a thread can be in more than one state. For example if a thread, a background thread and is currently running, then the state would be both running and background. The table below describes the possible states a thread can be in.


ThreadState Members































Member


Description


Aborted


The Thread has aborted


AbortRequested


A request has been made to abort a thread


Background


The Thread is executing as a background thread


Running


The thread is being executed


Suspended


The thread has been suspended


SuspendRequested


The Thread is being requested to suspend


Unstarted


The Thread has not been started


WatSleepJoin


The Thread is blocked on a call to wait sleep or join

 


Joining Threads


The Thread.Join Method waits for a thread to finish before continuing processing. This is useful if you create several threads that are supposed to accomplish a certain task, but before you a want the foreground application to continue, you need to ensure that all the threads you created are completed.


The following code demonstrates joining of threads:


using

System;


using

System.Threading;

public

class thrds

{


publicvoid Threader1()


{


for (int i = 0;i <1000;i++)


{


if (i == 5 )


{


Thread.Sleep(500);


Console.WriteLine("Thread1 Sleeping");


}


}


}


publicvoid Threader2()


{


for (int i = 0;i <1000;i++)


{


if (i == 5)


{


Thread.Sleep(500);


Console.WriteLine("Thread2 Sleeping");


}


}


}


}


public

class ThreadTest

{


publicstaticint Main(string<> args)


{


thrds test = new thrds();


Thread t1 = new Thread(new ThreadStart(test.Threader1));



t1.Start();


Thread t2 = new Thread(new ThreadStart(test.Threader2));


t2.Priority = ThreadPriority.Highest;


t2.Start();



t1.Join();


Console.WriteLine("Writing");


Console.ReadLine();


return 0;



}


}


Synchronizing threads


When Threads are running, they are sharing time with other running threads. If you have a method that is running on multiple threads, each thread has only several milliseconds of processor time before the operating system preempts the thread to give another thread time in the same method. If you are in the middle of the math statement, or in the middle of concatenating a name, your thread could very well be stopped for several milliseconds and another running thread may overwrite the data. Consider the following code:

Advertisment

{


int y;


int v;


for(int z = 0;z < 20; z++)


{


return y * v;


}


}


It is highly likely that during the loop, a running will stop to allow another thread a chance at this method. When you write multithreaded applications this happens frequently, so you need to know how to address this situation


The following code solves the problem:


Lock(this){


int y;


int v;


for(int z = 0;z < 20; z++)


{


return y * v;


}


}


When a thread reaches lock block, it waits until it can get an exclusive lock on the expression being evaluated before it attempts any further processing. This ensures that multiple threads cannot corrupt shared data.


The Monitor class enables synchronization using the Monitor, Enter, Monitor.TryEnter, and Monitor.Exit methods. After you have a lock on code region, you can use the Monitor.Wait , Monitor.Pulse and Monitor.PulseAll methods to determine if a thread should continue a lock or if any previously locked methods are now available. Wait releases the lock if it is held and waits to be notified. When wait is called, the lock is freed and it returns and obtains the lock again.


 Polling and Listening


Polling and Listening are two more instances that represent the usefulness of multithreading. Class libraries, such as System.Net.Sockets include a full range of multithreaded classes that can aid you in creating TCP and UDP Listeners.


The code below uses a timer callback to poll for files in a directory. If a file is found it is promptly deleted. The following sample code expects a C:\Poll Directory. The constructor for the TimeCallback class expects an address for the thread to execute on; an object datatype representing the state of the timer; a due time, which represents a period of time poll until; and a period, which is a millisecond variable indicating when the polling interval occurs.


using

System;


using

System.IO;

using

System.Threading;

namespace

Timer1

{



class Class1


{



publicstaticvoid Main()


{


Console.WriteLine("Checking directory updates every 2 Seconds.");


Console.WriteLine("Hit Enter to terminate the program.");


Timer timer = new Timer(new TimerCallback(CheckStatus),null,0,2000);


Console.ReadLine();


timer.Dispose();


}


staticvoid CheckStatus(Object State)


{


string<> str = Directory.GetFiles("C:\\Poll");


if(str.Length > 0)


{


for(int i = 0;i < str.Length;i++)


{


Console.WriteLine(str);


File.Delete(str);


}


}


Console.WriteLine("Directory Empty");


}


}


}


After Running this for a while and periodically copying a few files into the C:\Poll directory, the console output should look similar to that shown in the figure below:













tech-news