Difference between Synchronous and Asynchronous: जब कोई Calling Method किसी Method को Call करता है और उस Method के पूरी तरह से Execute हो जाने का Wait करता है तथा जब वह Method पूरी तरह से Execute हो जाता है, तब वह Calling Method आगे बढता है, तो इस प्रकार की Programming को Synchronous Programming कहते हैं और इस प्रकार के सभी Methods को Synchronous Method कहते हैं।
किसी भी Program का ये Default Behavior होता है और Multi-Threading के इस Chapter के अलावा हमने पिछले Chapter तक जितने भी Programs Create किए हैं, वे सभी Synchronous Program के Examples हैं।
लेकिन जब कोई Calling Method जिस Method को Call करता है, उस Method के पूरी तरह से Completely Execute होने का Wait नहीं करताए बल्कि Method को Call करके अपने स्वयं के अगले Codes को Process करने लगता है, तो इस प्रकार के Program को Asynchronous Program कहते हैं और इस तरह का Method, जिसके Complete होने के लिए Calling Method Wait नहीं करताए Asynchronous Method कहलाता है।
यानी Asynchronous Method अपना काम पूरा करने से पहले ही Program Control को Calling Method में Return कर देता है ताकि Calling Method के अन्य Statements का Execution हो सके।
C# में async/await नाम के दो नए Features को इसी प्रकार की जरूरतों को आसानी से पूरा करने के लिए Define किया गया है, जिनका प्रयोग करके हम बडी ही आसानी से अपने Program में ऐसे Asynchronous Methods Create कर सकते हैं, जिनके Complete होने का Wait Calling Method को नहीं करना पडता।
इस प्रकार से Asynchronous Programming में मूल रूप से तीन मुख्य Part होते हैं, जो आपस में मिलकर Asynchronous Programs Create करने में मदद करते हैं:
Calling Method
ये वह Method होता है, जो किसी async Method को Call करता है और Call करने के बाद अपने स्वयं के आगे के Codes का Execution करता है। यानी Call किए गए async Method के पूरी तरह से Execute होने का Wait नहीं करता।
जबकि async Method Same Thread में Different Task की तरह अथवा किसी दूसरे Thread की तरह Execute होकर अपना काम करता रहता है, जिसका Calling Method से Directly कोई सम्बंध नहीं होता।
async Method
जिस Method को हमें Asynchronous तरीके से Invoke कराना होता है, उसके साथ हम async Keyword का प्रयोग करके उसे Asynchronous Declare कर देते हैं। परिणामस्वरूप CLR उस Method के पूरी तरह से Execute होने के लिए Calling Method को Wait करने हेतु Schedule नहीं करता।
await Expression
इस Expression को async Method के अन्दर ही Use किया जाता है और ये उस Task को Specify करता है, जिसे Asynchronously Perform होना है। एक async Method में जितनी जरूरत हो उतने await Expressions को Use किया जा सकता है। हालांकि यदि हम किसी async Method में एक भी await Expression Use न करें, तो C# Compiler एक Warning Return करता है।
इस तरह से यदि हम Asynchronous Programming Model या Structure को एक चित्र के रूप में Represent करें, तो ये कुछ निम्नानुसार होता है:
async Method
जैसाकि हमने पिछले Section में बताया कि async Method एक ऐसा Method होता है, जिसे Calling Method Invoke तो करता है, लेकिन उसके Complete होने का Wait नहीं करताए बल्कि async Method को Call करने के तुरन्त बाद अपने आगे के Codes का Execution करने लगता है। किसी async Method की निम्न Characteristics होती हैं:
- इसके Header में async Modifier का प्रयोग किया गया होता है।
- इसकी Body में एक या अधिक await Expressions होते हैं, जो कि उन Tasks को Represent करते हैं, जिन्हें Asynchronously Execute करना होता है।
- ये void, Task या Task<T> Type में से किसी प्रकार का मान Return करता है। void के अलावा यदि ये Method Task या Task<T> Type का Object Return करता है, तो ये Task एक Statement Block को Represent करता है, जिसे Future में Program के किसी अन्य हिस्से में Execute किया जाना होता है।
- async Method में Variable Number of Arguments हो सकते हैं लेकिन कोई भी Argument ref या out नहीं हो सकता।
- Conventionally हर async Method के नाम का अन्त Async से होना चाहिए।
- Methods के अलावा हम Anonymous Method या Lambda Expressions को भी async Object की तरह Use कर सकते हैं।
किसी async Method के विभिन्न Components जिन्हें उपरोक्तानुसार Describe किया गया है, को हम निम्न चित्रानुसार Represent कर सकते हैं, जिससे इन्हें आसानी से समझा जा सके व याद रखा जा सके:
जैसाकि इस Code Segment में हम देख सकते हैं कि async Modifier का प्रयोग Return Type से पहले किया जाता है और इस Modifier का मतलब CLR को केवल इस बात का Instruction देना मात्र होता है कि ये एक Asynchronous Method है, जिसके पूरी तरह से Execute होने के लिए Calling Method को Wait करने की जरूरत नहीं है।
async Keyword एक Contextual Keyword है, जिसका मतलब ये है कि Method के साथ Modifier की तरह Specify करने के अलावा Program के किसी भी हिस्से में हम इसे Simple Identifier की तरह Declare करते हुए Use कर सकते हैं। हालांकि किसी भी तरह के Confusion से बचने के लिए हमें इसे कभी भी Identifier की तरह Declare नहीं करना चाहिए।
async Method कभी भी void, Task या Task<T> के अलावा अन्य किसी प्रकार का मान Return नहीं कर सकताए इस बात का हमेंशा ध्यान रखना चाहिए। तीनों ही प्रकार के Return Types के लिए हम निम्नानुसार तरीके से Code Segment Specify कर सकते हैं:
Flow of Control in Async Method
किसी भी async Method में Control Flow के मूल रूप से तीन Region होते हैं, जिसे निम्न Code Segment द्वारा समझा जा सकता है:
Before the first await expression
यहां हमें वे सारे Codes लिखने होते हैं जिनकी जरूरत सामान्य रूप से हमें आगे के Codes में पडने वाली है। यहां कोई ऐसा Code नहीं लिखना चाहिए, जिसे बहुत ज्यादा Processing की जरूरत हो।
The await expression
यहां हमें उस Expression को Specify करना होता है जो उस Task को Represent करता है, जिसे Asynchronously Perform करना होता है।
The continuation
बाकी के बचे हुए Codes हमें यहां यानी await Expression के बाद लिखने होते हैं।
उपरोक्त चित्र द्वारा हम async Method के Control Flow को आसानी से समझ सकते हैं। यानी सबसे पहले Control Synchronously Codes को Execute करते हुए Code Execution की शुरूआत करता है, जब तक कि उसे First await Modifier प्राप्त नहीं हो जाता। ये Region पहले ही Wait पर समाप्त हो जाता है। जबकि await Task अभी तक पूरी तरह से Complete नहीं हुआ होता। यदि await Task पहले ही Complete हो चुका हो, तो Method Synchronously Continue रहता है। साथ ही यदि फिर से कोई await मिलता है तो यही प्रक्रिया फिर से Repeat होती है।
जब await Expression पर Program Control पहुंच जाता है, तो Program Control फिर से Calling Method में Return होता है। जहां यदि Method का Return Type Task या Task<T> होता है, तो ये Method एक Task Object Create करता है जो कि उस Task को भी Represent करता है, जिसे Asynchronously Execute होना है और Continuation को भी Represent करता है तथा Calling Method को वह Task Object Return कर देता है।
अब Control के दो Flow होते हैं। पहले Flow में async Method होता है और दूसरे Flow में Calling Method होता है। यहां async Method निम्न काम करता है:
- ये Asynchronously Execute होता है और अपने await Expression के Awaitable Task को Asynchronously Execute करता है।
- जब await Expression पूरा हो जाता है, तो फिर Continuation Execute होता है। जबकि स्वयं Continuation में भी await Expression हो सकता है, जो कि इसी तरह से Handle होते हैं। यानी await Expression Asynchronously Execute होता है फिर Continuation Execute होता है।
- जब Program Control को Continuation में return Statement मिलता है या जब Method का End होता है, तब निम्न Actions Perform होते हैं:
- यदि Method का Return Type void हो, तो Control Method से Exit हो जाता है।
- यदि Method का Return Type Task हो, तो Control Task की Status Property Set करता है और Exit हो जाता है।
- यदि Method का Return Type Task<T> हो तो Continuation Task Object की Result Property को भी Set करने के बाद Return होता है।
इसी दौरान Calling Method का Code अपने स्तर पर Continually Execute होता रहता है और async Method से Task Object Receive करता है। जब Calling Method को async Method से Returned Task Object की Actual Value की जरूरत होती है, तो ये Task Object की Result Property को Use करता है तथा आगे का Code Execute करने लगता है।
अन्यथा यदि उसे Task Object से वांछित Result प्राप्त नहीं होताए तो वह Program को Halt कर देता है और async Method से Result प्राप्त करने का Wait करता है तथा Result प्राप्त होने के बाद Continually अपने Code को Run करने लगता है।
मुख्य रूप से ध्यान रखने वाली एक बात ये है कि जब Program Control को await Expression मिलता है, तो Method से Return होने वाले Object का Type वही होता है, जो async Modifier के बाद Specify किया गया होता है। फिर भले ही await Expression के साथ किसी भी प्रकार का मान Return क्यों न किया जा रहा हो। उदाहरण के लिए निम्न Code में await Statement site नाम का एक String Type का Object Return कर रहा है:
private async Task<int> CountCharactersAsync( string site ) { WebClient wc = new WebClient(); string result = await wc.DownloadStringTaskAsync( new Uri( site ) ); return result.Length; }
लेकिन जब ये Object Return होता है, तो Return होने वाला Object Task<int> Type का ही होता है, क्योंकि हमने async Method का Return Type Task<int> Specify किया है।
दूसरी ध्यान रखने वाली बात ये है कि जब async Method return Statement के माध्यम से कोई मान Return करता है, तो Program Control Actually Return नहीं होता बल्कि async Method से Exit होता है।
await Expression
await Expression उस Task को Represent करता है, जिसे Asynchronously Run करना है। await Modifier के Just बाद हमें एक Awaitable Object को Specify करना होता है, जिसे Task कहते हैं। ये Task, Task Type का Object हो भी सकता है और नहीं भी। Default रूप से ये Task Current Thread में ही Asynchronously Run होता है।
Awaitable Object, Awaitable Type का एक Instance होता है। जबकि Awaitable Type Instance में GetAwaiter() नाम का एक Method होता है, जो कोई Argument Accept नहीं करता लेकिन Awaiter Type का एक Object Return करता हैं, जिसके निम्न Members होते हैं:
bool IsCompleted {get; }
void OnCompleted(Action);
साथ ही इसमें निम्न में से कोई एक होता है:
void GetResult();
T GetResult();
वास्तव में हमें लगभग कभी भी अपना स्वयं का Awaitable बनाने की जरूरत नहीं पडती। बल्कि हमें Task Class को Use करना होता है, जो कि एक Awaitable होता है और हमारी लगभग सभी प्रकार की जरूरतें इसी से पूरी हो जाती हैं।
.NET 4.5 के साथ ही Microsoft ने BCL में बहुत सारे नए व Modified Asynchronous Methods Provide किए हैं, जो कि Task<T> Type का Object Return करते हैं। हम इन्हें सीधे ही अपने await Expression में Use कर सकते हैं और ये हमारे Current Thread पर Asynchronously Execute होते हैं। WebClient.DownloadStringTaskAsync भी Microsoft द्वारा Provided विभिन्न Task<T> Type Objects में से एक है, जिसे हम निम्नानुसार तरीके से Use कर सकते हैं:
हालांकि BCL हमें बहुत सारे ऐसे Methods Provide करता है, जो Task<T> Type का Object Return करते हैं। लेकिन फिर भी हम हमारा स्वयं का Method Create करते हैं, जिसे हम Task के रूप में await Expression के साथ Specify कर सकते हैं और ऐसा करने का सबसे सरल तरीका ये है कि हम Task.Run() Method का प्रयोग करते हुए एक Task Method Create करें। जबकि Task.Run() की एक सबसे महत्वपूर्ण बात ये है कि ये हमारे Task Method को एक Different Thread में Run करता है।
Task.Run() Method का एक Signature निम्नानुसार है, जो Func<TReturn> Delegate Type के एक Instance को Parameter की तरह Accept करता है। Func<TReturn> एक Predefined Delegate है जो कोई Parameter Accept नहीं करता लेकिन TReturn Type का एक Value Return करता है:
Task Run( Func<TReturn> function )
इसलिए यदि हम Task.Run() Method में अपना Custom Task Method Pass करना चाहते हैं, तो हमें इसका एक Delegate Create करना होता है और हम ऐसा तीन तरीकों से कर सकते हैं।
आगे दिए गए Code के आधार पर Get10() Method एक Func<int> Delegate Type Compatible Method है, जो कोई Parameter Accept नहीं करताए लेकिन एक Integer Return करता है:
File Name: async-await.cs using System; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using System.IO; namespace CSharpMultiThreading { class MyClass { public int Get10() // Func<int> compatible { return 10; } public async Task DoWorkAsync() { Func<int> ten = new Func<int>(Get10); int a = await Task.Run(ten); int b = await Task.Run(new Func<int>(Get10)); int c = await Task.Run(() => { return 10; }); Console.WriteLine("{0} {1} {2}", a, b, c); } } class Program { static void Main() { Task t = (new MyClass()).DoWorkAsync(); t.Wait(); } } } Output: 10 10 10
इस Code में हम देख सकते हैं कि हमने निम्नानुसार तीन तरीकों से Task.Run() Method को Use किया है:
int a = await Task.Run(ten); //Normal Function
int b = await Task.Run(new Func<int>(Get10)); //Anonymous Function
int c = await Task.Run(() => { return 10; }); //Lambda Expression
लेकिन Task.Run() Method के कुल 8 Overloaded Versions हैं, जो कि निम्नानुसार हैं और सभी अलग-अलग Signature के Custom Task Methods के लिए Use किए जाते हैं:
Return | Type Signature |
Task | Run( Action action ) |
Task | Run( Action action, CancellationToken token ) |
Task<TResult> | Run( Func<TResult> function ) |
Task<TResult> | Run( Func<TResult> function, CancellationToken token ) |
Task | Run( Func<Task> function ) |
Task | Run( Func<Task> function, CancellationToken token ) |
Task<TResult> | Run( Func<Task<TResult>> function ) |
Task<TResult> | Run( Func<Task<TResult>> function, CancellationToken token ) |
इसी तरह से जिन Delegates को Task.Run() Method में Pass किया जा सकता है, वे निम्नानुसार हैं:
Delegate Type | Delegate Signature | Meaning |
Action | void Action() | A method that takes no parameters and returns no value |
Func<TResult> | TResult Func() | A method that takes no parameters and returns an object of type T |
Func<Task> | Task Func() | A method that takes no parameters and returns a simple Task object |
Func<Task<TResult>> | Task<TResult> Func() | A method that takes no parameters and returns an object of type Task<T> |
आगे दिए गए चित्र में हमनें await Statement को चार अलग Delegate Types के साथ Task.Run() Method में Specify किया है:
await Expression को हम हमारे Program में कहीं पर भी Use कर सकते हैं, जहां किसी अन्य Expression को Use किया जा सकता है। शर्त बस यही है कि इसे किसी async Method के अन्दर ही Use किया जा सकता है। इस Code में हमने चार await Expressions को चार अलग Positions पर Use किए हैं।
जहां पहला व तीसरा Instance await Expression को एक Statement की तरह Use करता है। दूसरे Instance में await Expression का प्रयोग WriteLine() Method Call में Parameter की तरह Use किया है। जबकि चौथा Instance await Expression को Assignment Operator के Right Side में Use कर रहा है।
मानलो कि हमारे पास कोई ऐसा Task Method है जो उपरोक्त चारों Delegate Forms से Match नहीं करता। उदाहरण के लिए मानलो कि GetSum() Method दो Integer Arguments Accept करता है और उनका Total Return करता है।
तो ये GetSum() Method उपरोक्त चारों में से किसी भी Delegate से Match नहीं होगा। तो इस तरह के Custom Task Method को Use करने के लिए हम Acceptable Func Delegate में Lambda Expression की तरह प्रयोग कर सकते हैं, जो केवल GetSum() Method को Run करने का काम करेगा। जैसे:
int value = await Task.Run(() => GetSum(5, 6));
यहां Lambda Function () => GetSum(5,6), Func<TResult> Delegate को Satisfy कर देता है। क्योंकि ये एक ऐसा Delegate है जो कोई Parameter Accept नहीं करता, लेकिन एक Single Value Return करता है। इस पूरी प्रक्रिया को हम निम्न Program द्वारा बेहतर तरीके से समझ सकते हैं:
File Name: async-await-MisMatchDelegate.cs using System; using System.Threading; using System.Threading.Tasks; namespace CSharpMultiThreading { static class MyClass { private static int GetSum(int i1, int i2) { return i1 + i2; } public static async Task DoWorkAsync() { int value = await Task.Run(() => GetSum(5, 6)); Console.WriteLine(value.ToString()); } } class Program { static void Main() { Task t = MyClass.DoWorkAsync(); t.Wait(); Console.WriteLine("Press Enter key to exit"); Console.Read(); } } } Output: 11 Press Enter key to exit
ये Article इस वेबसाईट पर Selling हेतु उपलब्ध EBook C#.NET in Hindi से लिया गया है। इसलिए यदि ये Article आपके लिए उपयोगी रहा, तो निश्चित रूप से ये पुस्तक भी आपके लिए काफी उपयोगी साबित होगी।
C#.NET in Hindi | Page:908 | Format: PDF