System.Threading Namespace
.NET Platform में System.Threading Namespace के अन्तर्गत हमें बहुत सारे ऐसे Types Provide किए जाते हैं, जिनका प्रयोग करके हम बडी ही आसानी से Multi-Threaded .NET Applications Create कर सकते हैं।
साथ ही ये Namespace हमें ऐसे Types Provide करता है, जिनका प्रयोग करके हम किसी Particular CLR Thread के साथ Interact कर सकते हैं, CLR द्वारा Maintained Thread Poop को Access कर सकते हैं, Non-GUI Based Timer Class को Use कर सकते हैं और विभिन्न प्रकार के Types का प्रयोग करके Shared Resources को Synchronized तरीके से Share कर सकते हैं।
इस Namespace द्वारा Provided विभिन्न Members में से Thread नाम की Class सबसे महत्वपूर्ण है। क्योंकि यही Class हमें नए Threads Create करने व उन्हें Manage तथा Control करने से सम्बंधित विभिन्न प्रकार के जरूरी Features Provide करता है।
लेकिन किसी भी Multi-Threaded Program Create करने के लिए हमें इस Namespace को अपने Program में using Directive के माध्यम से Include करना जरूरी होता है। इस Namespace में Define किए गए विभिन्न Members की List निम्नानुसार है:
Thread Class
.NET का पूरा Multi-Threading System Thread Class पर ही आधारित है, जो कि “Thread of Execution” को Encapsulate करता है। Thread शब्द वास्तव में “Thread of Execution” का छोटा नाम है, जो किसी Currently Executing Code Block को Represent करता है।
Thread Class एक Sealed Class है। इसलिए इसे Inherit नहीं किया जा सकता। Thread Classes में बहुत सारे Methods व Properties को Defined किया गया है, जिनका प्रयोग करके हम हमारे Program के विभिन्न Threads को Manage व Control करते हैं और इस Section में हम इसी Class के विभिन्न Members के बारे में जानेंगे व उन्हें उपयोग में लेते हुए Multi-Threaded .NET Application Programs Create करना सीखेंगे।
Creating and Starting Thread
.NET में हम कई तरीकों से किसी Thread को Create व Start कर सकते हैं। लेकिन यहां हम बहुत ही Basic Mechanism के बारे में Discuss करेंगे, ताकि हम Multithreading Application Development को आसानी से समझ सकें।
कोई भी नया Thread Create करने के लिए हमें Thread Class का एक Object Create करना होता है और Thread को Start करने के लिए निम्नानुसार तरीके से Thread Class के एक Constructor को Call करना होता है:
public Thread(ThreadStart start);
जहां start उस Method को Specify करता है, जिसे Thread का Execution शुरू करने के लिए Call किया जा रहा है। दूसरे शब्दों में कहें तो ये Thread का Entry Point Specify करता है। जबकि ThreadStart एक Delegate है, जिसे .NET Framework में निम्नानुसार तरीके से Define किया गया है:
public delegate void ThreadStart();
यानी ये एक ऐसा Delegate है जो किसी भी ऐसे Method को Invoke कर सकता है, जो No-Argument No-Return Value Signature का हो।
सरल शब्दों में कहें, तो जब हम उपरोक्तानुसार Thread Constructor को Use करते हैं, तो हम वास्तव में ThreadStart Delegate के start Parameter के माध्यम से किसी Callback Method को Execute होने के लिए Specify कर रहे होते हैं। जो कि तब Execute होता है, जब हम हमारे द्वारा Create किए जा रहे Thread Object के साथ Start() Method को Invoke करते हैं।
public void Start();
जब हम Start() Method को Call कर लेते हैं, तो Thread Class का Object Create करते समय हमने जिस Method को ThreadStart Delegate Type Object में Assign किया था, वह Method तब तक Execute होता रहता है, जब तक कि Entry Point Method Return नहीं हो जाता।
जैसे ही Thread का Entry Point Method Return होता है, Thread Automatically Stop हो जाता है। जबकि यदि हम किसी Stop हो चुके Thread को Start करने की कोशिश करते हैं, तो .NET Platform हमें ThreadStateException नाम का Exception Return करता है।
Thread Create करने व उसे Start करने की पूरी प्रक्रिया को हम निम्नानुसार एक Example Program द्वारा बेहतर तरीके से समझ सकते हैं:
File Name: ThreadCreationgAndStarting.cs using System; using System.Threading; namespace CSharpMultiThreading { class ThreadBasics { static void Main(string[] args) { int id = Thread.CurrentThread.ManagedThreadId; // Print out the ID of the executing thread. Console.WriteLine("Main() invoked on thread {0}.", id); // Create New Thread. Thread newThread = new Thread(PrintHello); // Start Newly Created Thread. newThread.Start(); } public static void PrintHello() { int id = Thread.CurrentThread.ManagedThreadId; // Print out the ID of the executing thread. Console.WriteLine("Add() invoked on thread {0}.", id); // Pause to simulate a lengthy operation. Thread.Sleep(2000); Console.WriteLine("Hello! I am 2 Second Late. Sorrrrrrrrrry!"); } } } Output: Main() invoked on thread 1. Add() invoked on thread 3. Hello! I am 2 Second Late. Sorrrrrrrrrry!
इस Program में हमने सबसे पहले निम्न Statement द्वारा Thread Class का एक नया Object Create किया है और उस Object के Constructor में PrintHello() Method का नाम Parameter के रूप में Specify कर दिया है, जो कि ThreadStart Type के Delegate Object की Invocation List में Add हो जाता है:
// Create New Thread.
Thread newThread = new Thread(PrintHello);
फिर हमने निम्न Statement द्वारा इस newThread नाम के Object के साथ Start() Method को Invoke करके Thread को Run किया है:
// Start Newly Created Thread.
newThread.Start();
परिणामस्वरूप जैसे ही ये Statement Execute होता है, C# Compiler newThread Object द्वारा Referenced Heap Area में Stored ThreadStart Type के Delegate Object की Invocation List में Stored PrintHello() Method को एक नए Thread में Invoke कर देता है।
जब PrintHello() Method Invoke होता है, तब वह एक नए Thread Of Execution के रूप में Invoke होता है। इसीलिए जब ये Method Thread.Sleep() Method द्वारा 2000 Milliseconds के लिए Suspend होता है, जो कि इस बात का Indication है कि PrintHello() Method में Execute करने के लिए बहुत कुछ है, जिसमें समय लग रहा है, फिर भी Main() Method इस PrintHello() Method के पूरी तरह से Complete होने का Wait नहीं करता। क्योंकि ये Method एक अलग Thread में Invoke हुआ है।
साथ ही Main() Method व PrintHello() Method दोनों अलग Thread में Invoke हुए हैं, इस बात का Indication हमें दिखाई देने वाले Output से भी मिलता है, जहां Main() Method का Thread ID 1 है जबकि PrintHello() Method का Thead ID 3 है।
पिछले उदाहरण में हमने बहुत ही सरल तरीके से एक नया Thread Create करने के बारे में समझने की कोशिश की है। जैसाकि हमने पहले भी कहा था कि हम किसी Thread को कई तरीकों से Create कर सकते हैं, यहां हम उन तरीकों के बारे में जानेंगे, जिनसे किसी Secondary Thread को Create किया जा सकता है।
जब हम Programmatically कोई Secondary Thread Create करना चाहते हैं, तब हम बडी ही आसानी से निम्न Steps को Follow करते हुए Secondary Threads Create कर सकते हैं:
- सबसे पहले हमें जिस Code को एक अलग Thread के रूप में Execute करना होता है, उसे एक Method के रूप में Define करना होता है, जैसाकि पिछले उदाहरण Program में हमने PrintHello() नाम के Method को Define किया है।
- फिर हमें ParameterizedThreadStart या ThreadStart Delegate Type का एक Object Create करना होता है और उस पहले Step में Define किए गए Method का Address इस Delegate Object के Constructor में Pass करना होता है।
- फिर हमें Thread Class का एक Object Create करना होता है और इस Thread Class के Constructor में दूसरे Step में Create किए गए Delegate Object को Parameter की तरह Pass करना होता है। जब हम Thread Class का Object Create करते हैं, तो उसके Constructor में यदि हम चाहें तो सी/ो ही उस Method को Argument की तरह Specify कर सकते हैं, जिसके लिए अलग Thread Create करना है, जैसाकि हमने हमारे पिछले Program में Thread Constructor के अन्दर Parameter के रूप में हमने Directly PrintHello() Method को Specify कर दिया है।
- फिर Newly Created Thread Object के साथ विभिन्न Properties जैसे कि Name, Priority आदि द्वारा Newly Create होने वाले Thread को Initial Values से Initialize कर सकते हैं।
- और अन्त में हम हमारे उस Newly Created Thread Object के लिए Start() Method को Call करते हैं। जिससे दूसरे Step में Create किए गए Delegate Object में Stored Reference वाला Callback Method एक नए Thread में Execute होना शुरू हो जाता है।
उपरोक्त सभी 5 Steps में से दूसरे Step में Specified दो Distinct Delegate Types (ParameterizedThreadStart या ThreadStart) में से हम किसी को भी Use कर सकते हैं और हम यहां पर जिस भी Delegate Type को Use करते हैं, वह Delegate Type उस Method को Reference करता है, जिसे Secondary Thread के रूप में Execute करना होता है।
ThreadStart Delegate Type हर उस Method को Point कर सकता है, जो किसी तरह का कोई Parameter Accept नहीं करते और कोई Value Return नहीं करते। इस Delegate Type को सामान्यत: तब Use किया जाता है, जब हमें किसी Thread को Background में Run करना होता है।
जबकि यदि हमें किसी Parameter Accept करने वाले Method को Secondary Thread के रूप में Execute करना हो, तो हमें ParameterizedThreadStart Delegate Type के Object के माध्यम से उस Secondary Thread Callback Method को Specify करना होता है।
ये Delegate Type System.Object Type का एक Single Parameter Accept करता है और चूंकि C# में System.Object एक Top Level Class होता है, जो कि किसी भी Type को Reference कर सकता है साथ ही C# में सबकुछ Type ही होता है, इसलिए यदि हमें किसी Method में Multiple Parameters Pass करने हों, तो हम उसे एक Class या Structure के रूप में Specify करते हुए इस Delegate Object में Pass कर सकते हैं।
हालांकि यहां भी हम केवल ऐसे ही Methods को इस Delegate Type द्वारा Reference कर सकते हैं, जो कि कोई मान Return नहीं करते। यानी Argument But No-Return Value Type के Methods को ही इस Delegate Object द्वारा Reference किया जा सकता है।
Using ThreadStart Delegate
हमारा पिछला Program भी ThreadStart Delegate पर ही आधारित था। लेकिन उस Program में हमने ThreadStart Delegate Type का Object Create नहीं किया था। बल्कि Directly Thread Constructor में उस Method का Address Specify किया था, जिसके लिए अलग Thread Create करना था। लेकिन हम यहां पर जो Example Program Create कर रहे हैं, उसे हम Exactly पिछले सभी 5 Steps को Follow करते हुए ही Create कर रहे हैं:
File Name: UsingThreadStartDelegate.cs using System; using System.Threading; using System.Windows.Forms; namespace CSharpMultiThreading { public class Printer { //Step1: Method to be executed as Secondary Thread public void PrintNumbers() { // Display Thread info. Console.WriteLine("-> {0} is executing PrintNumbers()", Thread.CurrentThread.Name); // Print out numbers. Console.Write("Your numbers: "); for (int i = 0; i < 10; i++) { Console.Write("{0}, ", i); Thread.Sleep(2000); } } } class UsingThreadStartDelegate { static void Main(string[] args) { Console.Write("Do you want [1] or [2] threads? "); string threadCount = Console.ReadLine(); // Name the current thread. Thread primaryThread = Thread.CurrentThread; primaryThread.Name = "Primary"; // Display Thread info. Console.WriteLine("-> {0} is executing Main()", Thread.CurrentThread.Name); // Make worker class. Printer p = new Printer(); //Step2: ThreadStart Delegate Object ThreadStart secondaryThreadCode = new ThreadStart(p.PrintNumbers); switch (threadCount) { case "2": // Step3: Create New Thread. Thread backgroundThread = new Thread(secondaryThreadCode); // Step4: Initialize Properties with Newly Created Thread. backgroundThread.Name = "Secondary"; // Step5: Start the Newly Created Thread. backgroundThread.Start(); break; case "1": p.PrintNumbers(); break; default: Console.WriteLine("I don't know what you want...you get 1 thread."); goto case "1"; } // Do some additional work. MessageBox.Show("I'm busy!", "Work on main thread..."); } } }
इस Program में हमने Printer नाम की एक Class Create की है, PrintNumbers() Method को हमें Secondary Thread की तरह Execute करवाना हैं। जहां PrintNumbers() नाम का Method केवल एक Looping Statement के माध्यम से गिनती Print करता है।
लेकिन हर गिनती Print करने के बीच इसे 2 Seconds का समय लगता है, जो इस बात का Indication है कि ये Thread कोई बहुत ज्यादा Code Execute कर रहा है, जिसकी वजह से इसे Execute होने में समय लग रहा है।
जब हम इस Program को Run करते हैं तो सबसे पहले निम्न Code Execute होता है, जो User से 1 या 2 Input करने के लिए कहता है:
Console.Write(“Do you want [1] or [2] threads? “);
string threadCount = Console.ReadLine();
यदि User 1 Input करता है, तो ये Program केवल एक Thread Create करता है। जबकि यदि User 2 Input करता है, तो ये Program कुल दो Threads Create करता है। इसके अलावा किसी भी अन्य संख्या को ये Program Invalid Number मानता है। इस Code के Run होने के बाद निम्न Code Run होता है:
// Name the current thread.
Thread primaryThread = Thread.CurrentThread;
primaryThread.Name = “Primary”;
ये Code primaryThread नाम के Thread Reference Variable में Current Thread का Reference Store करता है और उसका नाम “Primary” Set कर देता है। फिर ये Program निम्नानुसार Statements द्वारा Printer Type का एक Object Create करता है:
// Make worker class.
Printer p = new Printer();
और इस Object के लिए PrintNumbers() Method को एक अलग Thread में Invoke करने हेतु निम्नानुसार ThreadStart Delegate Type का एक Object Create करता है:
//Step2: ThreadStart Delegate Object
ThreadStart secondaryThreadCode = new ThreadStart(p.PrintNumbers);
इस Code के Execution के बाद यदि User 2 Input करता है, तो Switch Statement का निम्नानुसार Code Execute होता है:
// Step3: Create New Thread.
Thread backgroundThread = new Thread(secondaryThreadCode);
जो पिछले Step में Create किए गए secondaryThreadCode नाम के ThreadStart Delegate Object के लिए एक नया Thread Create करता है और निम्न Statements द्वारा उस Newly Created Thread की Name Property को “Secondary” Value से Set कर देता है:
// Step4: Initialize Properties with Newly Created Thread.
backgroundThread.Name = “Secondary”;
फिर निम्न Statement Execute होता है, और backgroundThread नाम के पिछले Step में Create किए गए Thread को एक नए Thread में Start कर देता है।
// Step5: Start the Newly Created Thread.
backgroundThread.Start();
लेकिन यदि User Program की शुरूआत में 2 के स्थान पर केवल 1 ही Input करता है, तो Switch Statement का निम्नानुसार Statement Run होता है
p.PrintNumbers();
जो कि केवल एक ही Thread यानी Main() Method के Thread में Run होता है। जबकि यदि User 1 या 2 के अलावा अन्य कोई संख्या Input करता है, तो Switch Statement का निम्नानुसार Default Statement Execute हो जाता है:
Console.WriteLine(“I don’t know what you want…you get 1 thread.”);
जो कि User के लिए एक Message होता है कि उसने गलत Number Input किया है। जबकि Program की शुरूआत से ही एक Message Box Display हो जाता है, क्योंकि हमने Program के अन्त में निम्नानुसार Statement Specify किया है:
// Do some additional work.
MessageBox.Show(“I’m busy!”, “Work on main thread…”);
ये Dialog Box उसी स्थिति में दिखाई देता है जबकि हमने using Statement के साथ System.Windows.Forms Namespace को Current Program में Include किया हो।
Using ParameterizedThreadStart Delegate
जैसाकि हमने पहले भी कहा कि ThreadStart Delegate केवल उन्हीं Methods को Point कर सकता है जो कोई Parameter Accept नहीं करते। इसलिए जब हमें किसी ऐसे Method को अलग Thread के रूप में Invoke करना होता है, जो कि कोई Parameter Accept करता हो, तो हमें उस Method को Point करने के लिए ParameterizedThreadStart Delegate Type Object को Use करना होता है।
इसे समझने के लिए हम हमारे AsyncCallbackDelegate.cs Program को फिर से Re-Create कर सकते हैं, जिसे हमनें इसी Chapter में पिछे Create किया था। लेकिन इस बार हम ParameterizedThreadStart Delegate Type को Use कर रहे हैं और हमारा Modified Program कुछ निम्नानुसार हो सकता है:
File Name: UsingParameterizedThreadStartDelegate.cs using System; using System.Threading; using System.Windows.Forms; namespace CSharpMultiThreading { class AddParams { public int a, b; public AddParams(int numb1, int numb2) { a = numb1; b = numb2; } } class UsingParameterizedThreadStartDelegate { static void Add(object data) { if (data is AddParams) { Console.WriteLine("ID of thread in Add(): {0}", Thread.CurrentThread.ManagedThreadId); AddParams ap = (AddParams)data; Console.WriteLine("{0} + {1} is {2}", ap.a, ap.b, ap.a + ap.b); } } static void Main(string[] args) { Console.WriteLine("ID of thread in Main(): {0}", Thread.CurrentThread.ManagedThreadId); // Make an AddParams object to pass to the secondary thread. AddParams ap = new AddParams(10, 10); ParameterizedThreadStart secondaryThreadCode = new ParameterizedThreadStart(Add); Thread t = new Thread(secondaryThreadCode); t.Start(ap); // Force a wait to let other thread finish. Thread.Sleep(5); } } } // Output: ID of thread in Main(): 1 ID of thread in Add(): 3 10 + 10 is 20
जैसाकि इस Program के Output द्वारा हम समझ सकते हैं कि ये Program भी Exactly वही Output Return कर रहा है, जो AsyncCallbackDelegate.cs Program ने Return किया था। अन्तर केवल इतना है, कि इस Program को हमने Thread Class के आधार पर .NET के Multithreading Features का प्रयोग करते हुए Create किया है, जबकि AsyncCallbackDelegate.cs Program पूरी तरह से Delegates पर आधारित था।
इस Program में भी हमने दो संख्याओं को जोडने के लिए Add() नाम का एक Method Create किया है जबकि दोनों जुडने वाली संख्याऐं Data हैं जो कि Parameter की तरह ParameterizedThreadStart Delegate Object में Pass होंगे, इसलिए इन्हें एक Class के रूप में Define किया है। परिणामस्वरूप जब ये Program Run होता है, तो सबसे पहले निम्न Statement Execute होता है:
AddParams ap = new AddParams(10, 10);
इस Statement के माध्यम से हम AddParams Type का एक Object ap Create करते हैं और उसके Constructor में मान 10, 10 Input करते हैं, जो कि वे दो Integer Values हैं, जिन्हें जोडना है। लेकिन क्योंकि हम ParameterizedThreadStart Delegate Object में केवल एक System.Object Type का मान ही Parameter के रूप में Specify कर सकते हैं, इसलिए हमने हमारे दोनों Add होने वाले Integers को AddParams Type के Object ap में Store कर दिया है।
फिर निम्नानुसार Statement द्वारा ParameterizedThreadStart Delegate Type का एक Object Create किया है और उसमें Add() Method को Specify कर दिया है, जिसे हम एक Secondary Thread के रूप में Run करना चाहते हैं:
ParameterizedThreadStart secondaryThreadCode = new ParameterizedThreadStart(Add);
और निम्न Statement द्वारा इस Delegate Object को एक नया Thread Object Create करके उसके Constructor में Pass कर दिया है, जिसका Reference t में Stored है:
Thread t = new Thread(secondaryThreadCode);
जिसके साथ निम्नानुसार Start() Method को Invoke करके हमने हमारे Add() Method के लिए एक अलग Thread Create करके उसे Execute किया है:
t.Start(ap);
परिणामस्वरूप जैसे ही ये Statement Execute होता है, ParameterizedThreadStart Delegate में Stored Add() Method Execute हो जाता है और Parameter के रूप में AddParams Type के Object ap का Reference निम्नानुसार तरीके से object Type के Variable data में Store हो जाता है:
static void Add(object data) { if (data is AddParams) { Console.WriteLine("ID of thread in Add(): {0}", Thread.CurrentThread.ManagedThreadId); AddParams ap = (AddParams)data; Console.WriteLine("{0} + {1} is {2}", ap.a, ap.b, ap.a + ap.b); } }
जहां विभिन्न Logics का प्रयोग करके data नाम के Object Type के Object से दोनों Integer Values को Retrieve करके जोडा जाता है और Output Console में Display कर दिया जाता है।
इस प्रकार से जब हमें Parameterized Methods को Secondary Thread के रूप में Execute करना होता है, तब हम उपरोक्त Program के अनुसार Logics का प्रयोग करते हुए Parameterized Methods को अलग Thread में Execute कर सकते हैं, जो कि Internally ठीक उसी तरह से काम करता है, जिस तरह से AsyncCallbackDelegate.cs Program कर रहा था।
ये Article इस वेबसाईट पर Selling हेतु उपलब्ध EBook C#.NET in Hindi से लिया गया है। इसलिए यदि ये Article आपके लिए उपयोगी रहा, तो निश्चित रूप से ये पुस्तक भी आपके लिए काफी उपयोगी साबित होगी।
C#.NET in Hindi | Page:908 | Format: PDF