by Dinesh Thakur Category: C# Libraries

Like java, C# also provides built-in support for multithreading. A multithread program contains two or more parts that can run concurrently. Each part of such program is called thread, and each thread defines a separate path of execution. Thus, multithreading is a specialized form of multitasking.

Multithreading enables you to write very efficient programs that make maximum use of CPU, because idle time can be kept to a minimum. This especially important for the interactive, networked environment in which C# operates, because idle time is common. For Example, the transmission rate of data over a network is much slower than the rate at which the computer can process it. Even local file system resources are read and Written at much slower pace than they can be processed by the CPU. And, of course, user input is much slower than the computer. In traditional, single thread environment, your program has to wait for each of these tasks to finish before it can }?roceedto the next one-even though the CPU is sitting idle most of the time. Multithreading lets you gain access to this idle time and ‘put it good use.

Multitasking vs. Multithreading

Multitasking is the ability of an operating system to execute more than one program simultaneously. Though we say so but in reality no two programs on a single processor machine can be executed at the same time. The CPU switches from one program to the next so quickly that appears as if all of the programs are executing at the same time.

 

Multithreading is the ability of an operating system to execute the different parts of the program, called threads, simultaneously. The program has to be designed well so that the different threads do not interfere with each other. This concept helps to create scalable applications because you can add threads as and when needed.,

& Concept             

In Multitasking, operating system executes more than one program simultaneously, while in multithreading executes different parts of a program simultaneously.

 

Individual programs are all isolated from each other in terms of their memory and data, but individual threads are not as they all share the same memory and data variables. Hence, implementing multitasking is relatively easier in an operating system than implementing multithreading.

When to Use Multiple Threads

Software that requires user interaction must react to' the user's activities as rapidly as possible to provide a rich user experience. At the same time, however, it must do the calculations necessary to present data to the user as fast as possible. If your application uses only one thread of execution, you can combine asynchronous programming with .NET Framework remoting or XML'Web services created using ASP.NET to use the processing time of other computers in addition to that of your own to increase responsiveness to the user and decrease the data processing time of your application. If you are doing intensive input/output work, you can also use 110 completion ·ports to increase your application's responsiveness.

Advantages &Disadvantages

Advantages of Multiple threads

 

Using more than one thread, however, is the most powerful technique available to increase responsiveness to the user and process the data necessary to get the job done at almost the same time. On a computer with one processor, multiple threads can create this effect, taking a0dvantage of the. small periods of time in between user events' to process the data in. the background. For example, a user can edit a spreadsheet while another thread is recalculating other parts of the spreadsheet within the same application.

 

Without modification, the same application would dramatically increase user satisfaction when run on a computer with more than one processor. Your single application domain could use multiple threads to accomplish the following tasks:

 

• Communicate over a network, to a Web server, and to a database.

• Perform operations that take a large amount of time

• Distinguish tasks of varying priority. For example, a high-priority thread manages time- critical tasks, and a low-priority thread performs other tasks.

• Allow the user interface to remain responsive, while allocating time to background tasks.

 

Disadvantages of Multiple Threads

 

It is recommended that you use as few threads as possible, thereby minimizing the use of operating-system resources and improving performance. Threading also has resource requirements and potential conflicts to be considered when designing your application. The resource requirements are as follows:

• The system consumes memory for the context information required by processes,

AppDomain objects and threads. Therefore, the number of processes, AppDomain objects, and threads that can be created is limited by available memory.

• Keeping track of a large number of threads consumes significant processor time.- If there are too many threads, most of them will not make significant progress. If most of the current threads are in one process, threads in other processes are scheduled less frequently.

• Controlling code execution with many threads is complex, and can be a source of many bugs.

• Destroying threads requires knowing what could happen and handling those issues.

Providing shared access to resources can create conflicts. To avoid conflicts, you must synchronize, or control the access to, shared resources. Failure to synchronize access properly (in the same or different application domains) can lead to problems such as deadlocks (in which two threads stop responding while each waits for the other to complete) and race conditions (when an anomalous result. occurs due to an unexpected critical dependence on the timing of two events). The system provides synchronization objects that can be used to coordinate resource sharing among multiple threads. Reducing the number of threads makes it easier to synchronize resources.

Resources that require synchronization include:

• System resources (such as communications ports).

• Resources shared by multiple processes (such as file handles).

The resources of a single application domain(such as global, static, and instance fields)

accessed by multiple threads.

Working with Threads

The classes and interfaces in the System.Threading namespace provide the multithreadirig support in· the .NET platform. This namespace consists of a number of classes.

System.Threading.Thread is the main class for creating threads and controlling them.

 

The Thread class has a number of methods. A few interesting methods are shown below:

 

Start () : starts the execution of the thread.

Suspend(): suspends the thread, if the thread is already suspended, nothing happens.

Resume () : resumes a thread that has been suspended.

Interrupt(): interrupts a thread that is in the wait, sleep or join stage.

Join () : blocks a calling thread until the thread terminates.

sleep(int x) : suspends the thread for specified amount of time (in milliseconds).

Abort() : Begins the process of terminating the thread. Once the thread terminates, it cannot be ·restarted by calling the function Start() again.

This class also has a number of interesting properties as shown below:

ISAlive : if true, signifies that thread has been started and has not yet been terminatod or aborted

Name : gets/sets the name of the thread

priority :gets/sets the scheduling priority of a thread

ThreadState :gets a value containing the state of the current thread

Creating and Running Threads

To create thread first of all create new instance of Thread object.

Thread myThread = new Thread( new ThreadStart(myFunc) );

The constructor of Thread class accepts only one parameter that is delegate. Microsoft

CLR provides ThreadStart delegate class for starting the thread ..

To run this thread we need following

Thread tl = new Thread( new Threadstart(Incrementer) );

To instantiate this we need followings

t1.Start( );

 

Thread implementation

 

namespace Example

{

using System;

using system. Threading;

class Tester

{

static void Main( )

{

// make an instance of this class

Tester t = new Tester( );

// run outside static Main

t.DoTest( );

}

Public void DOTest( )

{

// create a thread for the Incrementer

// pass in a Threadstart delegate

// with the address of Incrementer

Thread,.t2 =new Thread(new ThreadStart(Incrementer? );

// create a thread for the Decrementer

// pass in a ThreadStart delegate

II with the address of decrementer

Thread t2 =new Thread(new Threadstart(Decrementer) );

// start the threads

tl.Start( );

t2.Start( );

}

// demo function, counts up to 1000

public void Incrementer( )

{

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

{

Console.writeLine("Incrementer: {o}", i);

}

}

// demo function, counts down from 1k

public void Decrementer( )

{

for (int i = 1000;i>=0;i--)

{

Console.WriteLine("Decrementer: {o}", i);

}

}

}

}

 

OUTPUT:

 

Incrementer:102

Incrementer:103

Incrementer:104

Incrementer:l05

Incrementer:l06

Decrementer:l000

Decrementer:999

Decrementer:998

Decrementer:997

 

The example, shown below, is a very simple one that discusses how to apply the thread properties.

To create a thread, we need to instantiate the Thread class, passing a ThreadStart delegate (System.Threading.ThreadStart) in its constructor. This delegate contains the method where the thread will begin execution, when started. The Start() method of the Thread class then starts the execution of a new thread. Let us understand the concept with a small example.

Thread Properties

using system;

using system.Threading

namespace LearnThreads

{

class Thread_App

{

public static void First_Thread()

{

console.writeLine("First thread created");

Thread current_thread = Thread.currentThread;

string thread_details = "Thread Name: " +

current_thread.Name +"\r\nThread State: "+

current_thread.Threadstate.ToString()+

"\r\n Thread priority level:"+

current_thread.priority.ToString();

console.writeLine("The details of the thread are :"+

thread_details);

console.writeLine ("first thread terminated");

}

public static void Main()

{

Threadstart thr_start_func = new Threadstart (First_Thread);

console.writeLine ("creating the first thread ");

Thread fThread = new Thread (thr_start_func);

fThread.Name= "first_thread";

fThread.Start (); //starting the thread

}

}

}

In this example, we are creating a new thread called fThread, which when started executes the function called First_Thread(). Note the use of the delegate called ThreadStart that contains the address of the function that needs to be executed when the thread's Start() is called.

Thread States

System.Threading.Thread.ThreadState property defines the state in which a thread is during the execution of the thread. Once created, a thread is always in at least one of the states till it is terminated. When a thread is created it is in Unstarted state. Start() method of the Thread class changes this state to a Running state for the thread. The thread continues to be in this state till it goes into a sleep or is suspended or is aborted or the thread terminates naturally. When the thread is suspended is goes into the Suspended state till another thread resumes it, when it is back again to the Running state. On being aborted or terminated, the thread is stopped and the ThreadState is Stopped. Once stopped, a thread can never leave the Stopped state just as once started a thread can never return to the Unstarted state.

There is another state called Background state, which indicates whether a thread is running in the foreground or background.

A thread can be in multiple states at a given point in time. Say, a thread is blocked on a call to Sleep and another threads calls Abort1)n the blocked thread. What happens?? The thread will be in both WaitSleepJoin and the AbortRequested states at the same time. As soon as the thread returns from a call to Sleep or is interrupted, the thread receives a ThreadAbortException to begin aborting.

Thread Priority

System. Threading. Thread. Priority enumeration defines the priority states of a thread that in turn determines how much processor time a thread gets to execute. Threads are executed based on their priority levels. High priority threads always get more processor time than their counterparts. If there is more than one thread at a high priority then the operating system cycles through the threads giving each high priority thread some amount of processor time. Lower priority threads do not get to execute as long as a higher priority thread is available. When there are no more threads of higher· priority, the operating system then selects the next lower priority threads for execution. If at any point it encounters a higher priority thread, \then the lower priority threads are preempted and give way to the higher priority thread to execute. Any new thread gets created with a Normal priority. You can then change the priority value to any of the values shown below (these are defined in the Priority property of the Thread class):

 

• Highest

• AboveNormal

• Normal

• BelowNormal

• Lowest·





About Dinesh Thakur

Dinesh ThakurDinesh Thakur holds an B.SC (Computer Science), MCSE, MCDBA, CCNA, CCNP, A+, SCJP certifications. Dinesh authors the hugely popular blog. Where he writes how-to guides around Computer fundamental , computer software, Computer programming, and web apps. For any type of query or something that you think is missing, please feel free to Contact us.