|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionAlthough C# contains many innovative features, one of the most exciting is its built in support for multithreaded programming. 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. - from Herbert Schildt's book "C# 2.0 The complete Reference". chepter 22 Multithreading FundamentalsThere 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. Simply we can define a thread as a line of execution within a process and it can exist in any of these several states. It can be running. It can be ready to run as soon as it gets CPU time. A running thread can be suspended, which is a temporary halt to its execution. It can later be resumed, A thread can be blocked when waiting for a resource. A thread can be terminated, in which case its execution ends and cannot be resumed. 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 “Foreground” threads have the ability to prevent the current application from terminating. The CLR will not shutdown an application until all foreground threads have ended. The “Background” threads 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 Thus, you will usually include this statement at the start of any multithreaded program: using System.Threading;
Creating a ThreadTo create a thread, you instantiate an object of type public Thread( ThreadStart entrypoint)
Here, entrypoint is the name of the method that will be called to begin execution of the thread. public delegate void ThreadStart()
Thus, your entrypoint method must have a Once created, the new thread will not start running until you call its public void Start()
Once started, the thread will run until the method specified by entryPoint returns. Thus, when entryPoint returns, the thread automatically stops. If you try to call Exampleusing System;
using System.Threading;
namespace CSharpThreadExample
{
class Program
{
public static void run()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine("In thread " + Thread.CurrentThread.Name + i);
Thread.Sleep(1000);
}
}
static void Main(string[] args)
{
Console.WriteLine("Main Thread Starting");
Thread.CurrentThread.Name = "Main ";
Thread t1 = new Thread(new ThreadStart(run));
t1.Name = "Child";
t1.Start();
for (int i = 0; i < 5; i++)
{
Console.WriteLine("In thread " + Thread.CurrentThread.Name + i);
Thread.Sleep(1000);
}
Console.WriteLine("Main Thread Terminates");
Console.Read();
}
}
}
Notice the call to public static void Sleep(int milliseconds)
The number of milliseconds to suspend is specified in milliseconds. If milliseconds is zero, the calling thread is suspended only to allow a waiting thread to execute. Here’s the output:
Each thread maintains a private set of structures that the O/S uses to save information(the thread context) when the thread is not running including the value of CPU registers. It also maintains the priority levels. We can assign higher priority to a thread of more important task than to a thread which works in background. In all cases time slice allocated to each thread is relatively short, so that the end user has the perception that all the threads (and all the applications) are running concurrently. O/S has thread scheduler which schedules existing threads and preempts the running thread when its time slice expires. We make the most use of multithreading when we allocate distinct threads to tasks that have different priorities or that take a lot of time to complete. The main problem with threads is that they compete for shared resource, a resource can be a variable, a database connection, a H/W device. We must synchronize the access to such resources — otherwise we will result in deadlock situations. A thread terminates when the task provided to it terminates or when the thread is programmatically killed by calling Methods
States of a ThreadStates of a thread can be checked using
It is wise to check the state of a thread before calling methods on it to avoid
This picture describes in detail about the states of the thread [ Collected from “Thinking in C#” by Bruce Eckel ] Properties of a Thread
PriorityLevels of ThreadPriority levels of thread is set or checked by using an enumeration i.e.
Synchronization in ThreadsWhen 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
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 KeywordIn C# we use Syntax: lock (objecttobelocked) {
objecttobelocked.somemethod();
}
Here If we want to lock down the code in a static method, we need to provide the Converting the Code to Enable Synchronization using the Lock Keywordpublic void PrintNumbers()
{
lock (this)
{
for (int i = 0; i < 5; i++)
{
Thread.Sleep(100);
Console.Write(i + ",");
}
Console.WriteLine();
}
}
OUTPUT
Using the Monitor TypeThe C# lock keyword is just a notation for using Converting the Code to Enable Synchronization using the Monitor Classpublic 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);
}
}
OUTPUT
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.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||