Delegates
C# delegates are similar to pointers to functions, in C or C++. A delegate is a reference type variable that holds the reference to a method. The reference can be changed at runtime.
Delegates are especially used for implementing events and the call-back methods. All delegates are implicitly derived from the System.Delegate class.
Declaring Delegates
Delegate declaration determines the methods that can be referenced by the delegate. A delegate can refer to a method, which have the same signature as that of the delegate.
For example, consider a delegate:
public delegate int MyDelegate (string s);
The preceding delegate can be used to reference any method that has a single string parameter and returns an int type variable.
Syntax for delegate declaration is:
delegate <return type> <delegate-name> <parameter list>
Instantiating Delegates
Once a delegate type has been declared, a delegate object must be created with the new keyword and be associated with a particular method. When creating a delegate, the argument passed to the newexpression is written like a method call, but without the arguments to the method. For example:
public delegate void printString(string s);
...
printString ps1 = new printString(WriteToScreen);
printString ps2 = new printString(WriteToFile);
Following example demonstrates declaration, instantiation and use of a delegate that can be used to reference methods that take an integer parameter and returns an integer value.
using System;
delegate int NumberChanger(int n);
namespace DelegateAppl
{
class TestDelegate
{
static int num = 10;
public static int AddNum(int p)
{
num += p;
return num;
}
public static int MultNum(int q)
{
num *= q;
return num;
}
public static int getNum()
{
return num;
}
static void Main(string[] args)
{
//create delegate instances
NumberChanger nc1 = new NumberChanger(AddNum);
NumberChanger nc2 = new NumberChanger(MultNum);
//calling the methods using the delegate objects
nc1(25);
Console.WriteLine("Value of Num: {0}", getNum());
nc2(5);
Console.WriteLine("Value of Num: {0}", getNum());
Console.ReadKey();
}
}
}
When the above code is compiled and executed, it produces following result:
Value of Num: 35
Value of Num: 175
Multicasting of a Delegate
Delegate objects can be composed using the "+" operator. A composed delegate calls the two delegates it was composed from. Only delegates of the same type can be composed. The "-" operator can be used to remove a component delegate from a composed delegate.
Using this useful property of delegates you can create an invocation list of methods that will be called when a delegate is invoked. This is called multicasting of a delegate. The following program demonstrates multicasting of a delegate:
using System;
delegate int NumberChanger(int n);
namespace DelegateAppl
{
class TestDelegate
{
static int num = 10;
public static int AddNum(int p)
{
num += p;
return num;
}
public static int MultNum(int q)
{
num *= q;
return num;
}
public static int getNum()
{
return num;
}
static void Main(string[] args)
{
//create delegate instances
NumberChanger nc;
NumberChanger nc1 = new NumberChanger(AddNum);
NumberChanger nc2 = new NumberChanger(MultNum);
nc = nc1;
nc += nc2;
//calling multicast
nc(5);
Console.WriteLine("Value of Num: {0}", getNum());
Console.ReadKey();
}
}
}
When the above code is compiled and executed, it produces following result:
Value of Num: 75
Use of Delegate
The following example demonstrates the use of delegate. The delegate printString can be used to reference methods that take a string as input and return nothing.
We use this delegate to call two methods, the first prints the string to the console, and the second one prints it to a file:
using System;
using System.IO;
namespace DelegateAppl
{
class PrintString
{
static FileStream fs;
static StreamWriter sw;
// delegate declaration
public delegate void printString(string s);
// this method prints to the console
public static void WriteToScreen(string str)
{
Console.WriteLine("The String is: {0}", str);
}
//this method prints to a file
public static void WriteToFile(string s)
{
fs = new FileStream("c:\\message.txt",
FileMode.Append, FileAccess.Write);
sw = new StreamWriter(fs);
sw.WriteLine(s);
sw.Flush();
sw.Close();
fs.Close();
}
// this method takes the delegate as parameter and uses it to
// call the methods as required
public static void sendString(printString ps)
{
ps("Hello World");
}
static void Main(string[] args)
{
printString ps1 = new printString(WriteToScreen);
printString ps2 = new printString(WriteToFile);
sendString(ps1);
sendString(ps2);
Console.ReadKey();
}
}
}
When the above code is compiled and executed, it produces following result:
The String is: Hello World
Events
Events are basically a user action like key press, clicks, mouse movements etc., or some occurrence like system generated notifications. Applications need to respond to events when they occur. For example, interrupts. Events are used for inter-process communication.
Events are basically a user action like key press, clicks, mouse movements etc., or some occurrence like system generated notifications. Applications need to respond to events when they occur. For example, interrupts. Events are used for inter-process communication.
Using Delegates with Events
The events are declared and raised in a class and associated with the event handlers using delegates within the same class or some other class. The class containing the event is used to publish the event. This is called the publisher class. Some other class that accepts this event is called the subscriberclass. Events use the publisher-subscriber model.
A publisher is an object that contains the definition of the event and the delegate. The event-delegate association is also defined in this object. A publisher class object invokes the event and it is notified to other objects.
A subscriber is an object that accepts the event and provides an event handler. The delegate in the publisher class invokes the method (event handler) of the subscriber class.
The events are declared and raised in a class and associated with the event handlers using delegates within the same class or some other class. The class containing the event is used to publish the event. This is called the publisher class. Some other class that accepts this event is called the subscriberclass. Events use the publisher-subscriber model.
A publisher is an object that contains the definition of the event and the delegate. The event-delegate association is also defined in this object. A publisher class object invokes the event and it is notified to other objects.
A subscriber is an object that accepts the event and provides an event handler. The delegate in the publisher class invokes the method (event handler) of the subscriber class.
Declaring Events
To declare an event inside a class, first a delegate type for the event must be declared. For example,
public delegate void BoilerLogHandler(string status);
Next, the event itself is declared, using the event keyword:
//Defining event based on the above delegate
public event BoilerLogHandler BoilerEventLog;
The preceding code defines a delegate named BoilerLogHandler and an event named BoilerEventLog, which invokes the delegate when it is raised.
To declare an event inside a class, first a delegate type for the event must be declared. For example,
public delegate void BoilerLogHandler(string status);
Next, the event itself is declared, using the event keyword:
//Defining event based on the above delegate public event BoilerLogHandler BoilerEventLog;
The preceding code defines a delegate named BoilerLogHandler and an event named BoilerEventLog, which invokes the delegate when it is raised.
Example 1:
using System;
namespace SimpleEvent
{
using System;
public class EventTest
{
private int value;
public delegate void NumManipulationHandler();
public event NumManipulationHandler ChangeNum;
protected virtual void OnNumChanged()
{
if (ChangeNum != null)
{
ChangeNum();
}
else
{
Console.WriteLine("Event fired!");
}
}
public EventTest(int n )
{
SetValue(n);
}
public void SetValue(int n)
{
if (value != n)
{
value = n;
OnNumChanged();
}
}
}
public class MainClass
{
public static void Main()
{
EventTest e = new EventTest(5);
e.SetValue(7);
e.SetValue(11);
Console.ReadKey();
}
}
}
When the above code is compiled and executed, it produces following result:
Event Fired!
Event Fired!
Event Fired!
using System; namespace SimpleEvent { using System; public class EventTest { private int value; public delegate void NumManipulationHandler(); public event NumManipulationHandler ChangeNum; protected virtual void OnNumChanged() { if (ChangeNum != null) { ChangeNum(); } else { Console.WriteLine("Event fired!"); } } public EventTest(int n ) { SetValue(n); } public void SetValue(int n) { if (value != n) { value = n; OnNumChanged(); } } } public class MainClass { public static void Main() { EventTest e = new EventTest(5); e.SetValue(7); e.SetValue(11); Console.ReadKey(); } } }
When the above code is compiled and executed, it produces following result:
Event Fired! Event Fired! Event Fired!
Example 2:
This example provides a simple application for troubleshooting for a hot water boiler system. When the maintenance engineer inspects the boiler, the boiler temperature and pressure is automatically recorded into a log file along with the remarks of the maintenance engineer.
using System;
using System.IO;
namespace BoilerEventAppl
{
// boiler class
class Boiler
{
private int temp;
private int pressure;
public Boiler(int t, int p)
{
temp = t;
pressure = p;
}
public int getTemp()
{
return temp;
}
public int getPressure()
{
return pressure;
}
}
// event publisher
class DelegateBoilerEvent
{
public delegate void BoilerLogHandler(string status);
//Defining event based on the above delegate
public event BoilerLogHandler BoilerEventLog;
public void LogProcess()
{
string remarks = "O. K";
Boiler b = new Boiler(100, 12);
int t = b.getTemp();
int p = b.getPressure();
if(t > 150 || t < 80 || p < 12 || p > 15)
{
remarks = "Need Maintenance";
}
OnBoilerEventLog("Logging Info:\n");
OnBoilerEventLog("Temparature " + t + "\nPressure: " + p);
OnBoilerEventLog("\nMessage: " + remarks);
}
protected void OnBoilerEventLog(string message)
{
if (BoilerEventLog != null)
{
BoilerEventLog(message);
}
}
}
// this class keeps a provision for writing into the log file
class BoilerInfoLogger
{
FileStream fs;
StreamWriter sw;
public BoilerInfoLogger(string filename)
{
fs = new FileStream(filename, FileMode.Append, FileAccess.Write);
sw = new StreamWriter(fs);
}
public void Logger(string info)
{
sw.WriteLine(info);
}
public void Close()
{
sw.Close();
fs.Close();
}
}
// The event subscriber
public class RecordBoilerInfo
{
static void Logger(string info)
{
Console.WriteLine(info);
}//end of Logger
static void Main(string[] args)
{
BoilerInfoLogger filelog = new BoilerInfoLogger("e:\\boiler.txt");
DelegateBoilerEvent boilerEvent = new DelegateBoilerEvent();
boilerEvent.BoilerEventLog += new
DelegateBoilerEvent.BoilerLogHandler(Logger);
boilerEvent.BoilerEventLog += new
DelegateBoilerEvent.BoilerLogHandler(filelog.Logger);
boilerEvent.LogProcess();
Console.ReadLine();
filelog.Close();
}//end of main
}//end of RecordBoilerInfo
}
When the above code is compiled and executed, it produces following result:
Logging info:
Temperature 100
Pressure 12
Message: O. K
This example provides a simple application for troubleshooting for a hot water boiler system. When the maintenance engineer inspects the boiler, the boiler temperature and pressure is automatically recorded into a log file along with the remarks of the maintenance engineer.
using System; using System.IO; namespace BoilerEventAppl { // boiler class class Boiler { private int temp; private int pressure; public Boiler(int t, int p) { temp = t; pressure = p; } public int getTemp() { return temp; } public int getPressure() { return pressure; } } // event publisher class DelegateBoilerEvent { public delegate void BoilerLogHandler(string status); //Defining event based on the above delegate public event BoilerLogHandler BoilerEventLog; public void LogProcess() { string remarks = "O. K"; Boiler b = new Boiler(100, 12); int t = b.getTemp(); int p = b.getPressure(); if(t > 150 || t < 80 || p < 12 || p > 15) { remarks = "Need Maintenance"; } OnBoilerEventLog("Logging Info:\n"); OnBoilerEventLog("Temparature " + t + "\nPressure: " + p); OnBoilerEventLog("\nMessage: " + remarks); } protected void OnBoilerEventLog(string message) { if (BoilerEventLog != null) { BoilerEventLog(message); } } } // this class keeps a provision for writing into the log file class BoilerInfoLogger { FileStream fs; StreamWriter sw; public BoilerInfoLogger(string filename) { fs = new FileStream(filename, FileMode.Append, FileAccess.Write); sw = new StreamWriter(fs); } public void Logger(string info) { sw.WriteLine(info); } public void Close() { sw.Close(); fs.Close(); } } // The event subscriber public class RecordBoilerInfo { static void Logger(string info) { Console.WriteLine(info); }//end of Logger static void Main(string[] args) { BoilerInfoLogger filelog = new BoilerInfoLogger("e:\\boiler.txt"); DelegateBoilerEvent boilerEvent = new DelegateBoilerEvent(); boilerEvent.BoilerEventLog += new DelegateBoilerEvent.BoilerLogHandler(Logger); boilerEvent.BoilerEventLog += new DelegateBoilerEvent.BoilerLogHandler(filelog.Logger); boilerEvent.LogProcess(); Console.ReadLine(); filelog.Close(); }//end of main }//end of RecordBoilerInfo }
When the above code is compiled and executed, it produces following result:
Logging info: Temperature 100 Pressure 12 Message: O. K
Generics:
Generics allow you to delay the specification of the data type of programming elements in a class or a method, until it is actually used in the program. In other words, generics allow you to write a class or method that can work with any data type.
You write the specifications for the class or the method, with substitute parameters for data types. When the compiler encounters a constructor for the class or a function call for the method, it generates code to handle the specific data type. A simple example would help understanding the concept:
using System;
using System.Collections.Generic;
namespace GenericApplication
{
public class MyGenericArray<T>
{
private T[] array;
public MyGenericArray(int size)
{
array = new T[size + 1];
}
public T getItem(int index)
{
return array[index];
}
public void setItem(int index, T value)
{
array[index] = value;
}
}
class Tester
{
static void Main(string[] args)
{
//declaring an int array
MyGenericArray<int> intArray = new MyGenericArray<int>(5);
//setting values
for (int c = 0; c < 5; c++)
{
intArray.setItem(c, c*5);
}
//retrieving the values
for (int c = 0; c < 5; c++)
{
Console.Write(intArray.getItem(c) + " ");
}
Console.WriteLine();
//declaring a character array
MyGenericArray<char> charArray = new MyGenericArray<char>(5);
//setting values
for (int c = 0; c < 5; c++)
{
charArray.setItem(c, (char)(c+97));
}
//retrieving the values
for (int c = 0; c< 5; c++)
{
Console.Write(charArray.getItem(c) + " ");
}
Console.WriteLine();
Console.ReadKey();
}
}
}
When the above code is compiled and executed, it produces following result:
0 5 10 15 20
a b c d e
Generics allow you to delay the specification of the data type of programming elements in a class or a method, until it is actually used in the program. In other words, generics allow you to write a class or method that can work with any data type.You write the specifications for the class or the method, with substitute parameters for data types. When the compiler encounters a constructor for the class or a function call for the method, it generates code to handle the specific data type. A simple example would help understanding the concept:using System; using System.Collections.Generic; namespace GenericApplication { public class MyGenericArray<T> { private T[] array; public MyGenericArray(int size) { array = new T[size + 1]; } public T getItem(int index) { return array[index]; } public void setItem(int index, T value) { array[index] = value; } } class Tester { static void Main(string[] args) { //declaring an int array MyGenericArray<int> intArray = new MyGenericArray<int>(5); //setting values for (int c = 0; c < 5; c++) { intArray.setItem(c, c*5); } //retrieving the values for (int c = 0; c < 5; c++) { Console.Write(intArray.getItem(c) + " "); } Console.WriteLine(); //declaring a character array MyGenericArray<char> charArray = new MyGenericArray<char>(5); //setting values for (int c = 0; c < 5; c++) { charArray.setItem(c, (char)(c+97)); } //retrieving the values for (int c = 0; c< 5; c++) { Console.Write(charArray.getItem(c) + " "); } Console.WriteLine(); Console.ReadKey(); } } }When the above code is compiled and executed, it produces following result:0 5 10 15 20 a b c d e
Features of Generics
Using generics is a technique that enriches your programs in the following ways:
-
It helps you to maximize code reuse, type safety, and performance.
-
You can create generic collection classes. The .NET Framework class library contains several new generic collection classes in the System.Collections.Generic namespace. You may use these generic collection classes instead of the collection classes in the System.Collectionsnamespace.
-
You can create your own generic interfaces, classes, methods, events and delegates.
-
You may create generic classes constrained to enable access to methods on particular data types.
-
You may get information on the types used in a generic data type at run-time by means of reflection.
Using generics is a technique that enriches your programs in the following ways:
It helps you to maximize code reuse, type safety, and performance.
You can create generic collection classes. The .NET Framework class library contains several new generic collection classes in the System.Collections.Generic namespace. You may use these generic collection classes instead of the collection classes in the System.Collectionsnamespace.
You can create your own generic interfaces, classes, methods, events and delegates.
You may create generic classes constrained to enable access to methods on particular data types.
You may get information on the types used in a generic data type at run-time by means of reflection.
Generic Methods
In the previous example, we have used a generic class; we can declare a generic method with a type parameter. The following program illustrates the concept:
using System;
using System.Collections.Generic;
namespace GenericMethodAppl
{
class Program
{
static void Swap<T>(ref T lhs, ref T rhs)
{
T temp;
temp = lhs;
lhs = rhs;
rhs = temp;
}
static void Main(string[] args)
{
int a, b;
char c, d;
a = 10;
b = 20;
c = 'I';
d = 'V';
//display values before swap:
Console.WriteLine("Int values before calling swap:");
Console.WriteLine("a = {0}, b = {1}", a, b);
Console.WriteLine("Char values before calling swap:");
Console.WriteLine("c = {0}, d = {1}", c, d);
//call swap
Swap<int>(ref a, ref b);
Swap<char>(ref c, ref d);
//display values after swap:
Console.WriteLine("Int values after calling swap:");
Console.WriteLine("a = {0}, b = {1}", a, b);
Console.WriteLine("Char values after calling swap:");
Console.WriteLine("c = {0}, d = {1}", c, d);
Console.ReadKey();
}
}
}
When the above code is compiled and executed, it produces following result:
Int values before calling swap:
a = 10, b = 20
Char values before calling swap:
c = I, d = V
Int values after calling swap:
a = 20, b = 10
Char values after calling swap:
c = V, d = I
In the previous example, we have used a generic class; we can declare a generic method with a type parameter. The following program illustrates the concept:using System; using System.Collections.Generic; namespace GenericMethodAppl { class Program { static void Swap<T>(ref T lhs, ref T rhs) { T temp; temp = lhs; lhs = rhs; rhs = temp; } static void Main(string[] args) { int a, b; char c, d; a = 10; b = 20; c = 'I'; d = 'V'; //display values before swap: Console.WriteLine("Int values before calling swap:"); Console.WriteLine("a = {0}, b = {1}", a, b); Console.WriteLine("Char values before calling swap:"); Console.WriteLine("c = {0}, d = {1}", c, d); //call swap Swap<int>(ref a, ref b); Swap<char>(ref c, ref d); //display values after swap: Console.WriteLine("Int values after calling swap:"); Console.WriteLine("a = {0}, b = {1}", a, b); Console.WriteLine("Char values after calling swap:"); Console.WriteLine("c = {0}, d = {1}", c, d); Console.ReadKey(); } } }When the above code is compiled and executed, it produces following result:Int values before calling swap: a = 10, b = 20 Char values before calling swap: c = I, d = V Int values after calling swap: a = 20, b = 10 Char values after calling swap: c = V, d = I
Generic Delegates
You can define a generic delegate with type parameters. For example:
delegate T NumberChanger<T>(T n);
The following example shows use of this delegate:
using System;
using System.Collections.Generic;
delegate T NumberChanger<T>(T n);
namespace GenericDelegateAppl
{
class TestDelegate
{
static int num = 10;
public static int AddNum(int p)
{
num += p;
return num;
}
public static int MultNum(int q)
{
num *= q;
return num;
}
public static int getNum()
{
return num;
}
static void Main(string[] args)
{
//create delegate instances
NumberChanger<int> nc1 = new NumberChanger<int>(AddNum);
NumberChanger<int> nc2 = new NumberChanger<int>(MultNum);
//calling the methods using the delegate objects
nc1(25);
Console.WriteLine("Value of Num: {0}", getNum());
nc2(5);
Console.WriteLine("Value of Num: {0}", getNum());
Console.ReadKey();
}
}
}
When the above code is compiled and executed, it produces following result:
Value of Num: 35
Value of Num: 175
You can define a generic delegate with type parameters. For example:delegate T NumberChanger<T>(T n);The following example shows use of this delegate:using System; using System.Collections.Generic; delegate T NumberChanger<T>(T n); namespace GenericDelegateAppl { class TestDelegate { static int num = 10; public static int AddNum(int p) { num += p; return num; } public static int MultNum(int q) { num *= q; return num; } public static int getNum() { return num; } static void Main(string[] args) { //create delegate instances NumberChanger<int> nc1 = new NumberChanger<int>(AddNum); NumberChanger<int> nc2 = new NumberChanger<int>(MultNum); //calling the methods using the delegate objects nc1(25); Console.WriteLine("Value of Num: {0}", getNum()); nc2(5); Console.WriteLine("Value of Num: {0}", getNum()); Console.ReadKey(); } } }When the above code is compiled and executed, it produces following result:Value of Num: 35 Value of Num: 175
Threads:
A thread is defined as the execution path of a program. Each thread defines a unique flow of control. If your application involves complicated and time consuming operations then it is often helpful to set different execution paths or threads, with each thread performing a particular job.
Threads are lightweight processes. One common example of use of thread is implementation of concurrent programming by modern operating systems. Use of threads saves wastage of CPU cycle and increase efficiency of an application.
So far we have written programs where a single thread runs as a single process which is the running instance of the application. However, this way the application can perform one job at a time. To make it execute more than one task at a time, it could be divided into smaller threads.
A thread is defined as the execution path of a program. Each thread defines a unique flow of control. If your application involves complicated and time consuming operations then it is often helpful to set different execution paths or threads, with each thread performing a particular job.Threads are lightweight processes. One common example of use of thread is implementation of concurrent programming by modern operating systems. Use of threads saves wastage of CPU cycle and increase efficiency of an application.So far we have written programs where a single thread runs as a single process which is the running instance of the application. However, this way the application can perform one job at a time. To make it execute more than one task at a time, it could be divided into smaller threads.
Thread Life Cycle
The life cycle of a thread starts when an object of the System.Threading.Thread class is created and ends when the thread is terminated or completes execution.
Following are the various states in the life cycle of a thread:
-
The Unstarted State: it is the situation when the instance of the thread is created but the Start method has not been called.
-
The Ready State: it is the situation when the thread is ready to run and waiting CPU cycle.
-
The Not Runnable State: a thread is not runnable, when:
-
Sleep method has been called
-
Wait method has been called
-
Blocked by I/O operations
-
The Dead State: it is the situation when the thread has completed execution or has been aborted.
The life cycle of a thread starts when an object of the System.Threading.Thread class is created and ends when the thread is terminated or completes execution.Following are the various states in the life cycle of a thread:
The Unstarted State: it is the situation when the instance of the thread is created but the Start method has not been called.
The Ready State: it is the situation when the thread is ready to run and waiting CPU cycle.
The Not Runnable State: a thread is not runnable, when:
- Sleep method has been called
- Wait method has been called
- Blocked by I/O operations
The Dead State: it is the situation when the thread has completed execution or has been aborted.
The Main Thread
In C#, the System.Threading.Thread class is used for working with threads. It allows creating and accessing individual threads in a multithreaded application. The first thread to be executed in a process is called the main thread.
When a C# program starts execution, the main thread is automatically created. The threads created using the Thread class are called the child threads of the main thread. You can access a thread using the CurrentThread property of the Thread class.
The following program demonstrates main thread execution:
using System;
using System.Threading;
namespace MultithreadingApplication
{
class MainThreadProgram
{
static void Main(string[] args)
{
Thread th = Thread.CurrentThread;
th.Name = "MainThread";
Console.WriteLine("This is {0}", th.Name);
Console.ReadKey();
}
}
}
When the above code is compiled and executed, it produces following result:
This is MainThread
In C#, the System.Threading.Thread class is used for working with threads. It allows creating and accessing individual threads in a multithreaded application. The first thread to be executed in a process is called the main thread.When a C# program starts execution, the main thread is automatically created. The threads created using the Thread class are called the child threads of the main thread. You can access a thread using the CurrentThread property of the Thread class.The following program demonstrates main thread execution:using System; using System.Threading; namespace MultithreadingApplication { class MainThreadProgram { static void Main(string[] args) { Thread th = Thread.CurrentThread; th.Name = "MainThread"; Console.WriteLine("This is {0}", th.Name); Console.ReadKey(); } } }When the above code is compiled and executed, it produces following result:This is MainThread
Commonly Used Properties and Methods of the Thread Class
The following table shows some of the most commonly used properties of the Thread class:
The following table shows some of the most commonly used properties of the Thread class:
Property | Description |
---|---|
CurrentContext | Gets the current context in which the thread is executing. |
CurrentCulture | Gets or sets the culture for the current thread. |
CurrentPrinciple | Gets or sets the thread's current principal (for role-based security). |
CurrentThread | Gets the currently running thread. |
CurrentUICulture | Gets or sets the current culture used by the Resource Manager to look up culture-specific resources at run time. |
ExecutionContext | Gets an ExecutionContext object that contains information about the various contexts of the current thread. |
IsAlive | Gets a value indicating the execution status of the current thread. |
IsBackground | Gets or sets a value indicating whether or not a thread is a background thread. |
IsThreadPoolThread | Gets a value indicating whether or not a thread belongs to the managed thread pool. |
ManagedThreadId | Gets a unique identifier for the current managed thread. |
Name | Gets or sets the name of the thread. |
Priority | Gets or sets a value indicating the scheduling priority of a thread. |
ThreadState | Gets a value containing the states of the current thread. |
The following table shows some of the most commonly used methods of the Thread class:
S.N | Method Name & Description |
---|---|
1 | public void Abort() Raises a ThreadAbortException in the thread on which it is invoked, to begin the process of terminating the thread. Calling this method usually terminates the thread. |
2 | public static LocalDataStoreSlot AllocateDataSlot() Allocates an unnamed data slot on all the threads. For better performance, use fields that are marked with the ThreadStaticAttribute attribute instead. |
3 | public static LocalDataStoreSlot AllocateNamedDataSlot( string name) Allocates a named data slot on all threads. For better performance, use fields that are marked with the ThreadStaticAttribute attribute instead. |
4 | public static void BeginCriticalRegion() Notifies a host that execution is about to enter a region of code in which the effects of a thread abort or unhandled exception might jeopardize other tasks in the application domain. |
5 | public static void BeginThreadAffinity() Notifies a host that managed code is about to execute instructions that depend on the identity of the current physical operating system thread. |
6 | public static void EndCriticalRegion() Notifies a host that execution is about to enter a region of code in which the effects of a thread abort or unhandled exception are limited to the current task. |
7 | public static void EndThreadAffinity() Notifies a host that managed code has finished executing instructions that depend on the identity of the current physical operating system thread. |
8 | public static void FreeNamedDataSlot(string name) Eliminates the association between a name and a slot, for all threads in the process. For better performance, use fields that are marked with the ThreadStaticAttribute attribute instead. |
9 | public static Object GetData( LocalDataStoreSlot slot ) Retrieves the value from the specified slot on the current thread, within the current thread's current domain. For better performance, use fields that are marked with the ThreadStaticAttribute attribute instead. |
10 | public static AppDomain GetDomain() Returns the current domain in which the current thread is running. |
11 | public static AppDomain GetDomain() Returns a unique application domain identifier |
12 | public static LocalDataStoreSlot GetNamedDataSlot( string name ) Looks up a named data slot. For better performance, use fields that are marked with the ThreadStaticAttribute attribute instead. |
13 | public void Interrupt() Interrupts a thread that is in the WaitSleepJoin thread state. |
14 | public void Join() Blocks the calling thread until a thread terminates, while continuing to perform standard COM and SendMessage pumping. This method has different overloaded forms. |
15 | public static void MemoryBarrier() Synchronizes memory access as follows: The processor executing the current thread cannot reorder instructions in such a way that memory accesses prior to the call to MemoryBarrier execute after memory accesses that follow the call to MemoryBarrier. |
16 | public static void ResetAbort() Cancels an Abort requested for the current thread. |
17 | public static void SetData( LocalDataStoreSlot slot, Object data ) Sets the data in the specified slot on the currently running thread, for that thread's current domain. For better performance, use fields marked with the ThreadStaticAttribute attribute instead. |
18 | public void Start() Starts a thread. |
19 | public static void Sleep( int millisecondsTimeout ) Makes the thread pause for a period of time. |
20 | public static void SpinWait( int iterations ) Causes a thread to wait the number of times defined by the iterations parameter |
21 | public static byte VolatileRead( ref byte address ) public static double VolatileRead( ref double address ) public static int VolatileRead( ref int address ) public static Object VolatileRead( ref Object address ) Reads the value of a field. The value is the latest written by any processor in a computer, regardless of the number of processors or the state of processor cache. This method has different overloaded forms. Only some are given above. |
22 | public static void VolatileWrite( ref byte address, byte value ) public static void VolatileWrite( ref double address, double value ) public static void VolatileWrite( ref int address, int value ) public static void VolatileWrite( ref Object address, Object value ) Writes a value to a field immediately, so that the value is visible to all processors in the computer. This method has different overloaded forms. Only some are given above. |
23 | public static bool Yield() Causes the calling thread to yield execution to another thread that is ready to run on the current processor. The operating system selects the thread to yield to. |
Creating Threads
Threads are created by extending the Thread class. The extended Thread class then calls the Start()method to begin the child thread execution.
The following program demonstrates the concept:
using System;
using System.Threading;
namespace MultithreadingApplication
{
class ThreadCreationProgram
{
public static void CallToChildThread()
{
Console.WriteLine("Child thread starts");
}
static void Main(string[] args)
{
ThreadStart childref = new ThreadStart(CallToChildThread);
Console.WriteLine("In Main: Creating the Child thread");
Thread childThread = new Thread(childref);
childThread.Start();
Console.ReadKey();
}
}
}
When the above code is compiled and executed, it produces following result:
In Main: Creating the Child thread
Child thread starts
Threads are created by extending the Thread class. The extended Thread class then calls the Start()method to begin the child thread execution.The following program demonstrates the concept:using System; using System.Threading; namespace MultithreadingApplication { class ThreadCreationProgram { public static void CallToChildThread() { Console.WriteLine("Child thread starts"); } static void Main(string[] args) { ThreadStart childref = new ThreadStart(CallToChildThread); Console.WriteLine("In Main: Creating the Child thread"); Thread childThread = new Thread(childref); childThread.Start(); Console.ReadKey(); } } }When the above code is compiled and executed, it produces following result:In Main: Creating the Child thread Child thread starts
Managing Threads
The Thread class provides various methods for managing threads.
The following example demonstrates the use of the sleep() method for making a thread pause for a specific period of time.
using System;
using System.Threading;
namespace MultithreadingApplication
{
class ThreadCreationProgram
{
public static void CallToChildThread()
{
Console.WriteLine("Child thread starts");
// the thread is paused for 5000 milliseconds
int sleepfor = 5000;
Console.WriteLine("Child Thread Paused for {0} seconds",
sleepfor / 1000);
Thread.Sleep(sleepfor);
Console.WriteLine("Child thread resumes");
}
static void Main(string[] args)
{
ThreadStart childref = new ThreadStart(CallToChildThread);
Console.WriteLine("In Main: Creating the Child thread");
Thread childThread = new Thread(childref);
childThread.Start();
Console.ReadKey();
}
}
}
When the above code is compiled and executed, it produces following result:
In Main: Creating the Child thread
Child thread starts
Child Thread Paused for 5 seconds
Child thread resumes
The Thread class provides various methods for managing threads.The following example demonstrates the use of the sleep() method for making a thread pause for a specific period of time.using System; using System.Threading; namespace MultithreadingApplication { class ThreadCreationProgram { public static void CallToChildThread() { Console.WriteLine("Child thread starts"); // the thread is paused for 5000 milliseconds int sleepfor = 5000; Console.WriteLine("Child Thread Paused for {0} seconds", sleepfor / 1000); Thread.Sleep(sleepfor); Console.WriteLine("Child thread resumes"); } static void Main(string[] args) { ThreadStart childref = new ThreadStart(CallToChildThread); Console.WriteLine("In Main: Creating the Child thread"); Thread childThread = new Thread(childref); childThread.Start(); Console.ReadKey(); } } }When the above code is compiled and executed, it produces following result:In Main: Creating the Child thread Child thread starts Child Thread Paused for 5 seconds Child thread resumes
Destroying Threads
The Abort() method is used for destroying threads.
The runtime aborts the thread by throwing a ThreadAbortException. This exception cannot be caught, the control is sent to the finally block, if any.
The following program illustrates this:
using System;
using System.Threading;
namespace MultithreadingApplication
{
class ThreadCreationProgram
{
public static void CallToChildThread()
{
try
{
Console.WriteLine("Child thread starts");
// do some work, like counting to 10
for (int counter = 0; counter <= 10; counter++)
{
Thread.Sleep(500);
Console.WriteLine(counter);
}
Console.WriteLine("Child Thread Completed");
}
catch (ThreadAbortException e)
{
Console.WriteLine("Thread Abort Exception");
}
finally
{
Console.WriteLine("Couldn't catch the Thread Exception");
}
}
static void Main(string[] args)
{
ThreadStart childref = new ThreadStart(CallToChildThread);
Console.WriteLine("In Main: Creating the Child thread");
Thread childThread = new Thread(childref);
childThread.Start();
//stop the main thread for some time
Thread.Sleep(2000);
//now abort the child
Console.WriteLine("In Main: Aborting the Child thread");
childThread.Abort();
Console.ReadKey();
}
}
}
When the above code is compiled and executed, it produces following result:
In Main: Creating the Child thread
Child thread starts
0
1
2
In Main: Aborting the Child thread
Thread Abort Exception
Couldn't catch the Thread Exception
The Abort() method is used for destroying threads.The runtime aborts the thread by throwing a ThreadAbortException. This exception cannot be caught, the control is sent to the finally block, if any.The following program illustrates this:using System; using System.Threading; namespace MultithreadingApplication { class ThreadCreationProgram { public static void CallToChildThread() { try { Console.WriteLine("Child thread starts"); // do some work, like counting to 10 for (int counter = 0; counter <= 10; counter++) { Thread.Sleep(500); Console.WriteLine(counter); } Console.WriteLine("Child Thread Completed"); } catch (ThreadAbortException e) { Console.WriteLine("Thread Abort Exception"); } finally { Console.WriteLine("Couldn't catch the Thread Exception"); } } static void Main(string[] args) { ThreadStart childref = new ThreadStart(CallToChildThread); Console.WriteLine("In Main: Creating the Child thread"); Thread childThread = new Thread(childref); childThread.Start(); //stop the main thread for some time Thread.Sleep(2000); //now abort the child Console.WriteLine("In Main: Aborting the Child thread"); childThread.Abort(); Console.ReadKey(); } } }When the above code is compiled and executed, it produces following result:In Main: Creating the Child thread Child thread starts 0 1 2 In Main: Aborting the Child thread Thread Abort Exception Couldn't catch the Thread Exception
No comments:
Post a Comment