Threading in C#: Multi-Threaded Programming, C# का .NET Platform Supported एक बहुत ही Powerful Built-In Feature है, जिसमें एक ही Program में दो या दो से अधिक हिस्से होते हैं, जो कि समानान्तर रूप से Run होते हैं। Program के इन सभी Parts को Thread कहा जाता है और हर Thread का अपना अलग Execution Path होता है। अत: Multi-Threading वास्तव में Multi-Tasking का ही Specialized रूप है।
Multi-Threaded Programming पूरी तरह से C# Programming Language के Defined Features व .NET Framework की Predefined Classes का Combination है। क्योंकि Multi-Threading Support को C# Programming Language में Predefined कर दिया गया है।
इसलिए अन्य Programming Languages की तुलना में C# में Multi-Threading से सम्बंधित विभिन्न प्रकार की समस्याओं को Minimize या Eliminate कर दिया गया है। परिणामस्वरूप C# में Multi-Threaded Applications Develop करना किसी भी अन्य Programming Language की तुलना में ज्यादा आसान है।
.NET Framework 4.0 के Release के साथ ही Multi-Threaded Applications के लिए Task Parallel Library (TPL) व Parallel LINQ( PLINQ) नाम के दो और नए Features को Add किया गया है। ये दोनों ही Features Parallel Programming को Support करते हैं और Multiple-Processor (Multi-Core) Based Computers द्वारा Provide किए जाने वाले विभिन्न Advantages का पूरा फायदा उठाते हैं। साथ ही TPL का प्रयोग करने की वजह से हम ज्यादा आसानी से Multi-Threaded Applications Develop व Manage करने की क्षमता प्राप्त करते हैं। Multi-Threaded Applications Create करने के लिए TPL Based Multi-Threading Approach को अब Recommended Approach के रूप में Use किया जाता है।
मूल रूप से दो तरह के Multi-Tasking Types होते हैं, जिन्हें Process-Based Multi-Tasking व Thread-Based Multi-Tasking के नाम से जाना जाता है। Process और Thread के अन्तर को हम इस Chapter की शुरूआत में ही Clear कर चुके हैं कि कोई भी Running .NET Application एक Process होता है जबकि एक Process में कई Threads हो सकते हैं।
अत: Process Based Multi-Tasking में कोई Machine एक समय में एक से ज्यादा Programs को Concurrently Run करता है। उदाहरण के लिए यदि हम Tally और MS-Word दोनों को एक ही समय पर Run करें, तो ये दो अलग Processes होने की वजह से इसे Process Based Multi-Tasking कहेंगे। जबकि एक Thread Based Multi-Tasking में एक ही Program के कई हिस्से होते हैं, जो समानान्तर रूप से Run होते हैं।
Thread Based Multi-Tasking में सभी Processes में कम से कम एक Thread होता है, जबकि उनमें एक से ज्यादा Threads भी हो सकते हैं। जिसका मतलब ये है कि एक ही Program एक ही समय पर एक से ज्यादा तरह के काम कर सकता है। यानी एक ही Program किसी Document की Printing भी कर सकता है और उसी समय पर कोई Mathematical Calculate भी Perform कर सकता है।
यदि सारांश के रूप में समझें तो Process Based व Thread Based Multi-Tasking के बीच अन्तर ये है कि Process Based Multi-Tasking Programs के Concurrent Execution को Handle करता है। जबकि Thread Based Multi-Tasking किसी Program के विभिन्न Parts के Concurrent Execution को Handle करता है।
एक Thread किसी भी समय विभिन्न States में से किसी एक State में होता है। यानी कोई Thread Running State में हो सकता है, Ready to Run State में हो सकता है, Suspended State में हो सकता है, Blocked State में हो सकता है या फिर Terminated State में हो सकता है।
.NET Framework दो तरह के Threads Define कर सकता है, जिन्हें Foreground व Background Threads के नाम से जाना जाता है। Default रूप से जब हम कोई Thread Create करते हैं, तो उसे Foreground Thread कहा जाता है। लेकिन हम इस Thread को एक Background Thread में Convert कर सकते हैं।
Background Thread व Foreground Thread में यही मुख्य अन्तर है Background Thread उस समय Automatically Terminate हो जाता है, जब सभी Foreground Threads का Process Stop हो चुका होता है।
Thread-Based Multi-Tasking का प्रयोग एक Special Type की Requirement को पूरा करने के लिए किया जाता है, जिसे Synchronization कहते हैं। Synchronization हमें Threads को एक Well-Defined तरीके से Coordinate करते हुए Execute करने की सुविधा देता है। C# हमें Synchronization से सम्बंधित एक Complete Subsystem Provide करता है।
किसी Process में कम से कम एक Thread जरूर होता है, जो सामान्यत: Main Thread कहलाता है। क्योंकि ये उस समय Automatically Execute हो जाता है, जब हमारा Program होना शुरू होता है। अत: अभी तक हमने जितने भी Program Create किए हैं, उन सभी में केवल Main Thread ही Exist था। इसी Main Thread को Primary Thread कहा जाता है और इसी Primary Thread से हम अन्य Secondary Threads Create कर सकते हैं।
C# और .NET Framework दोनों ही Process-Based व Thread-Based दोनों प्रकार के Multi-Tasking को Support करते हैं। अत: C# में हम Process व Thread दोनों को Create व Manage कर सकते हैं और इसी Chapter के पिछले Section में हमने Processes के बारे में काफी Detail से जाना है। अब हम Multi-Threaded programming के बारे में जानेंगे और Multi-Threaded Programs Create करने के लिए हमें .NET Platform के System.Threading Namespace को उपयोग में लेना जरूरी होता है क्योंकि Threading से सम्बंधित सभी जरूरी Features इसी Namespace में Available हैं।
Threading in C# – Process, AppDomain and Context
Thread किसी Executable Application के अन्दर Execution का एक Path Define करता है। हालांकि विभिन्न Single-Threaded .NET Applications Normal तरीके से Execute व Run होते हैं। लेकिन किसी .NET Application Assembly का Primary Thread, Program के Execution के दौरान किसी भी समय Secondary Thread Create कर सकता है। Additional Threads Create करके हम ज्यादा Responsive Application Create करते हैं, लेकिन किसी Single-Core Machine पर वह Application Fast भी हो ऐसा जरूरी नहीं है।
.NET 1.0 में ही Multi-Threading Support प्राप्त करने के लिए System.Threading नाम का एक Namespace बनाया गया था और Multi-Threaded Program Create करने के लिए एक Standard Approach Provide किया गया था, जिसके आधार पर हम Multi-Threaded .NET Applications Develop कर सकते हैं। इस Namespace में Thread नाम की एक Core Class को Define किया गया है, जो कि किसी Application के विभिन्न Threads को Represent करता है।
इसलिए यदि हम किसी Program के Currently Execute हो रहे किसी Thread का Reference Programmatically प्राप्त करना चाहें, तो हम Thread.CurrentThread नाम के Static Member को Use कर सकते हैं। जैसे:
Thread myThread = Thread.CurrentThread
.NET Platform में Application Domain व Threads के बीच कोई Direct One-to-One Relationship नहीं है। वास्तव में किसी Specified AppDomain में भी एक ही समय पर बहुत सारे Threads हो सकते हैं जो कि समानान्तर रूप से Run हो रहे हों। इसी तरह से कोई Particular Thread किसी एक ही Single AppDomain में Exist रहेगा, ऐसा भी नहीं होताए बल्कि कोई Thread अपनी Lifetime के दौरान Current Process के विभिन्न AppDomains के बीच Exist हो सकता है।
यानी Threads Cross Application Domain Boundaries से Free होते हैं और Windows Operating System का Thread Scheduler तथा .NET CLR जिस AppDomain में जिस Thread को चाहें, Schedule कर सकते हैं।
हालांकि Active Threads विभिन्न AppDomain Boundaries के बीच Move हो सकते हैं, फिर भी कोई Specific Thread किसी एक समय पर किसी एक Single Application में ही Execute हो सकता है। यानी कोई एक Thread एक ही समय पर एक से ज्यादा AppDomains में Execute नहीं हो सकता।
जब हम किसी ऐसे AppDomain का Programmatically Reference Retrieve करना चाहते हैं, जिसमें कोई Specific Thread Hosted होता है, तब हम इस जरूरत को पूरा करने के लिए Thread.GetDomain() Method को Invoke कर सकते हैं, जो कि Thread Class का Member Method है। जैसे:
AppDomain ad = Thread.GetDomain();
हम किसी भी समय किसी Single Thread को किसी Particular Context में भी Move कर सकते हैं और किसी भी समय उस Thread को CLR में Relocate कर सकते हैं। जिस समय कोई Thread Execute हो रहा होता है, यदि उसी समय हमें उस Thread का Current Context प्राप्त करना हो, तो हम Thread.CurrentContext Property को Use कर सकते हैं जो कि System.Runtime.Remoting.Contexts.Context Type का Object Return करता है। जैसे:
Context ctx = Thread.CurrentContext;
यहां हमेंशा ध्यान रखने वाली बात यही है कि केवल CLR ही किसी Specific Thread को किसी Specific Application Domain व Context में Move कर सकता है और कोई नहीं। हमें इस बात की चिन्ता करने की जरूरत नहीं होती कि किस Thread को किस AppDomain में Place किया गया है या किस Object Context Boundary में Place किया गया है। लेकिन हम .NET Framework द्वारा Provided विभिन्न Classes के माध्यम से किसी भी समय इस बात को जान सकते हैं, कि कौनसा Thread किस AppDomain या Context में Execute हो रहा है।
Multi-Threaded Programming की एक सबसे बडी समस्या ये है कि Create होने वाले Thread पर हमारा किसी भी तरह का कोई Control नहीं होता। बल्कि Thread को पूरी तरह से CLR व Underlying Operating System ही Control करता है। इसलिए जब हम कोई Thread Create करते हैं, तो Thread Create होते ही वह Execute हो जाएगा, इस बात की कोई Guarantee नहीं होती क्योंकि कोई Thread कब Execute होगा, ये बात पूरी तरह से CLR व Operating System तय करते हैं।
साथ ही Threads को CLR द्वारा जरूरत के आधार पर किसी Process के अन्दर विभिन्न AppDomains व Contextual Boundaries के बीच Move किया जा सकता है। इसलिए हमें हमेंशा इस बात का ध्यान रखना होता है कि Application का कौनसा हिस्सा Thread-Volatile है जो कि Thread के Access Level को Define करता है और कौनसा हिस्सा Atomic है, जिस पर Thread-Volatile Operations Perform करना बिल्कुल भी उपयुक्त नहीं है।
उदाहरण के लिए यदि कोई Thread किसी Specific Object के किसी Method को Invoke करता है और हम ये मान लें कि इस Thread को Thread Scheduler द्वारा अपनी Activity को Suspend करने का Instruction मिलता है, ताकि कोई दूसरा Thread उसी Object के उसी Method को Access कर सके।
लेकिन जिस समय ये इस Thread को Suspension Order मिलता है, उस समय तक वह Thread पूरी तरह से Finish नहीं हुआ होता। इस स्थिति में वास्तव में दूसरा Thread किसी अवांछित Data को Read कर रहा होता है, जो कि एक बहुत ही मुि”कल से Debug होने वाला Bug है।
जबकि Atomic Operations पूरी तरह से किसी Multi-Threaded Environment में Safe होते हैं। लेकिन परेशानी ये है कि .NET Library में ऐसे Operations बहुत ही कम हैं, जो कि निश्चित रूप से Atomic होते हैं। यहां तक कि किसी Member Variable को Value Assign करना भी Atomic नहीं होता। इसलिए जब .NET Framework 4.5 SDK Documentation कहता है कि कोई Operation Atomic है, तो इसका यही मतलब है कि वह Precautions पर आधारित Thread-Volatile है।
उपरोक्त Discussion से ये बात तो निश्चित है कि सभी .NET Multi-Threaded Programs स्वयं Volatile होते हैं और विभिन्न Threads समान समय पर Shared Data पर Operate हो सकते हैं। इसलिए किसी .NET Application Resource को किसी भी तरह के Corruption से बचाने के लिए एक .NET Developer के रूप में हमें विभिन्न प्रकार के Threading Primitives जैसे कि Locks, Monitors व [Synchronization] Attribute या Language Keyword (async / await) Support को Use करना जरूरी होता है, ताकि हम Execute होने वाले विभिन्न Threads के Access Level को Control कर सकें।
हालांकि .NET Platform 4.0 के साथ ही TPL Library को Include किया गया है, जिसकी वजह से .NET Framework का प्रयोग करते हुए Multithreaded Applications Create करना काफी आसान हो गया है।
साथ ही .NET 4.5 द्वारा Add किए गए C# async व await Language Keywords द्वारा हम Minimum Botheration से Multiple Threads के साथ प्रक्रिया करने की सुविधा प्राप्त कर सकते हैं, जबकि पहले हमें Normal Working Multi-Threaded Program बनाने के लिए विभिन्न प्रकार की बातों का ध्यान रखना जरूरी होता था।
इससे पहले कि हम System.Threading Namespace, TPL व C# के async व await Keywords के बारे में चर्चा करना शुरू करें, हम पहले .NET Delegate Type को इस बात के लिए समझेंगे कि हम किस तरह से किसी Method को Asynchronous Manner में Invoke कर सकते हैं।
हालांकि हम .NET 4.5 के C# Keyword async व await Keywords का प्रयोग करके इस काम को आसानी से कर सकते हैं जो कि Asynchronous Delegates के Alternatives हैं, लेकिन फिर भी Asynchronous Delegates के बारे में जानना जरूरी है ताकि हम समझ सकें कि विभिन्न प्रकार की जरूरतों को पूरा करने के लिए हम किस तरह से Asynchronous Delegates को Use कर सकते हैं और C# 5.0 में Add किए गए async व await Keywords Internally कैसे काम करते हैं।
ये Article इस वेबसाईट पर Selling हेतु उपलब्ध EBook C#.NET in Hindi से लिया गया है। इसलिए यदि ये Article आपके लिए उपयोगी रहा, तो निश्चित रूप से ये पुस्तक भी आपके लिए काफी उपयोगी साबित होगी।
C#.NET in Hindi | Page:908 | Format: PDF