C# Task Example

C# in Hindiये Article इस वेबसाईट पर Selling हेतु उपलब्‍ध EBook C#.NET in Hindi से लिया गया है। इसलिए यदि ये Article आपके लिए उपयोगी रहा, तो निश्चित रूप से ये पुस्तक भी आपके लिए काफी उपयोगी साबित होगी। 

C#.NET in Hindi | Page:908 | Format: PDF

BUY NOW DOWNLOAD READ ONLINE

C# Task Example: Task Class TPL की मुख्‍य Core Class है। क्योंकि TPL में विभिन्न Execution Units को Tasks में Divide किया जाता है, Threads में नहीं। Task और Thread दो अलग Concepts हैं। Task एक ऐसा Abstraction होता है जो किसी Asynchronous Operation को Represent करता है। जबकि Thread किसी Thread of Execution को Encapsulate करता है।

हालांकि System Level पर एक Thread भी Execute होने वाले Statements के Set का एक Basic Unit ही होता है, जिसे Execute होने के लिए Operating System द्वारा Schedule किया जाता है। फिर भी Tasks Instance Thread of Execution के बीच One to One का सम्बंध नहीं होता।

Task Execution को एक Task Scheduler द्वारा Manage किया जाता है, जो कि Thread Pool के साथ मिलकर काम करता है। जिसका मतलब ये है कि एक से ज्यादा Tasks एक ही Thread में Share हो सकते हैं। इसलिए Thread Task हालांकि लगभग एक समान काम करते हैं, फिर भी एक दूसरे से पूरी तरह से अलग तरीके से काम करते हैं।

Creating Task

Task Create करने व Created Task का Execution Start करने के कई तरीके हैं। लेकिन हम एक Constructor के माध्‍यम से एक Constructor Create करेंगे और Start() Method का प्रयोग करके उस Task को Start करेंगे। ठीक उसी तरह से जिस तरह से हमने पहले एक Thread Create किया था और फिर Start() Method का प्रयोग करके उस Thread को Start किया था। Task Class में Task() Constructor के कई Versions Define किए गए हैं, जिनमें से एक निम्नानुसार है:

public Task(Action action)

यहां action उस Code का Entry Point है, जो कि किसी Task (Executable Code Block) को Represent कर रहा है। जबकि Action एक Delegate है, जिसे System Namespace में Define किया गया है। इस Delegate के भी कई रूप हो सकते हैं, क्योंकि ये एक Generic Delegate है। लेकिन हम यहां पर इस Delegate के निम्नानुसार No-Argument No-Return Value Form को Use करेंगे:

public delegate void Action()

जिसका मतलब ये है कि Task का Entry Point एक ऐसा Method होगा, जो न तो कोई Parameter Accept करता है न ही कोई Value Return करता है।

जब एक बार किसी Task को Create कर लिया जाता है, तो फिर उस Task को Start() Method को Call करके Start किया जा सका है। परिणामस्वरूप जब हम किसी Task को Start() Method का प्रयोग करके Start कर देते हैं, तो फिर Task Scheduler उस Task को Execute करने के लिए Schedule करने का काम करता है।

File Name: CreatingAndStartingTaks.cs
using System;
using System.Threading;
using System.Threading.Tasks;

namespace CSharpMultiThreading
{
    class Program
    {
        static void PrintHelloTask()
        {
            Thread.Sleep(50);
            Console.WriteLine("PrintHelloTask executing for Secondary Thread#{0} on TickCount: {1}", Thread.CurrentThread.ManagedThreadId, Environment.TickCount);
        }

        static void Main()
        {
            Console.WriteLine("Main Thread ID: {0}", Thread.CurrentThread.ManagedThreadId);

            // Create and Start ten new threads.
            for (int i = 0; i < 10; i++)
            {
                new Task(PrintHelloTask).Start();
            }
            Console.ReadLine();
        }
    }
}

Output:
Main Thread ID: 1
PrintHelloTask executing for Secondary Thread#3 on TickCount: 437821953
PrintHelloTask executing for Secondary Thread#4 on TickCount: 437821953
PrintHelloTask executing for Secondary Thread#3 on TickCount: 437822016
PrintHelloTask executing for Secondary Thread#4 on TickCount: 437822016
PrintHelloTask executing for Secondary Thread#3 on TickCount: 437822063
PrintHelloTask executing for Secondary Thread#4 on TickCount: 437822063
PrintHelloTask executing for Secondary Thread#4 on TickCount: 437822109
PrintHelloTask executing for Secondary Thread#3 on TickCount: 437822109
PrintHelloTask executing for Secondary Thread#4 on TickCount: 437822156
PrintHelloTask executing for Secondary Thread#3 on TickCount: 437822156

जैसाकि हम इस Program व इसके Output द्वारा समझ सकते हैं कि ये Program Exactly ConcurrencyControlWithLocks-Synchronization.cs के समान ही है। लेकिन इस Program में हमने Thread नहीं बल्कि निम्नानुसार तरीके से Task Create किया है:

new Task(PrintHelloTask).Start();

इस Statement में हमने Short तरीके से सीधे ही Task() Constructor को Call किया है और उस Method का नाम Parameter के रूप में Specify कर दिया है, जिसे Task की तरह Execute करना है।

चूंकि ये Statement एक Anonymous Task Object Create करता है, जिसका कोई नाम नहीं होताए इसलिए इस Constructor द्वारा Create होने वाले Anonymous Object के साथ Start() Method को Call करके अपने Task को Start भी कर दि; है।

हालांकि हमारा Program ठीक उसी तरह से Run हो रहा है, जिस तरह से Thread Based ConcurrencyControlWithLocks-Synchronization.cs Program Run हो रहा था, लेकिन जैसाकि हम Output में देख सकते हैं कि इस Program में केवल दो ही Secondary Threads Create हो रहे हैं, जिनमें Executable Task की दस बार Switching हो रही है, क्योंकि हमने Main() Method में 10 Task Create किए हैं।

ऐसा इसीलिए हो रहा है क्योंकि Current Computer एक Dual Core Processor पर आधारित Machine है। इसलिए TPL स्वं; ही दो Threads Create करता है जो कि Processor के दोनों Core पर Task को समान रूप से Divide करके Execute करता है, जिससे हमारे Program की Performance Threads की तुलना में काफी Improve हो जाती है। Task के Concept को और बेहतर तरीके से समझने के लिए हम निम्नानुसार एक और Demo Program Create कर सकते हैं:

File Name: CreatingAndStartingTaksDemo.cs
using System;
using System.Threading;
using System.Threading.Tasks;

namespace CSharpMultiThreading
{
    class DemoTask
    {
        // A method to be run as a task.
        static void MyTask()
        {
            Console.WriteLine("MyTask() starting");
            for (int count = 0; count < 10; count++)
            {
                Thread.Sleep(500);
                Console.WriteLine("In MyTask(), count is " + count);
            }
            Console.WriteLine("MyTask terminating");
        }

        static void Main()
        {
            Console.WriteLine("Main thread starting.");

            // Construct a task.
            Task tsk = new Task(MyTask);

            // Run the task.
            tsk.Start();

            // Keep Main() alive until MyTask() finishes.
            for (int i = 0; i < 60; i++)
            {
                Console.Write(".");
                Thread.Sleep(100);
            }
            Console.WriteLine("Main thread ending.");
        }
    }
}

Output:
Main thread starting.
.MyTask() starting
.....In MyTask(), count is 0
....In MyTask(), count is 1
.....In MyTask(), count is 2
.....In MyTask(), count is 3
.....In MyTask(), count is 4
.....In MyTask(), count is 5
.....In MyTask(), count is 6
.....In MyTask(), count is 7
.....In MyTask(), count is 8
.....In MyTask(), count is 9
MyTask terminating
..........Main thread ending.

इस Program में हमने MyTask() नाम का एक Method Create किया है, जिसे Task की तरह Run होना है।

जब इस Program का Main() Method Run होता है, तो सबसे पहले निम्नानुसार Statement द्वारा इस बात का Instruction Print होता है कि Main Thread Start हो रहा है:

Console.WriteLine(“Main thread starting.”);

फिर निम्न Statement द्वारा एक नया Task Create होता है:

// Construct a task.
Task tsk = new Task(MyTask);

जहां Class के Constructor में Parameter की तरह MyTask() Method के नाम को Specify किया गया है, जो कि Action नाम के Delegate Object में Store हो जाता है। फिर निम्न Statement द्वारा इस Newly Created Task को Start किया जाता है:

// Run the task.
tsk.Start();

परिणामस्वरूप MyTask() Method का Execution शुरू होता है और निम्न Statement Execute होता है:

Console.WriteLine(“MyTask() starting”);

जो इस बात का Indication होता है कि Task Start हो गया है। फिर बार-बार Main Thread व MyTask के Thead के बीच Thread.Sleep() Method के कारण Switching होती है और जब Control Main() Method में आता है, तो एक Dot Print करता है, जबकि MyTask() Method में जाने पर निम्नानुसार Statement के माध्‍यम से एक Message Print करता है:

Console.WriteLine(“In MyTask(), count is ” + count);

अन्त में जब MyTask() Method पूरी तरह से Execute हो जाता है, तब अन्तिम Statement के रूप में निम्न Statement Execute होता है:

Console.WriteLine(“MyTask terminating”);

जबकि Program Control के इस MyTask() Method से निकलने के बाद निम्नानुसार Main() Method का अन्तिम Statement Execute होता है:

Console.WriteLine(“Main thread ending.”);

चूंकि सभी Task हमेंशा Background Thread में ही Execute होते हैं, इसलिए ये जरूरी है कि Task समाप्त होने से पहले Main Thread Terminate न हो। इसीलिए हमने Main() Method में Thread.Sleep() Method को निम्नानुसार तरीके से Specify किया है:

            // Keep Main() alive until MyTask() finishes.
            for (int i = 0; i < 60; i++)
            {
                Console.Write(".");
                Thread.Sleep(100);
            }

यदि हम Main() Method में इस Looping Statement को न लिखें, तो Main() Thread, MyTask() के Background Thread से पहले Terminate हो जाएगा। परिणामस्वरूप MyTask() Method पूरी तरह से Execute हुआ हो या न हुआ हो, Background Thread होने की वजह से Foreground Thread के Terminate होते ही वह भी Terminate हो जाएगा।

Threads की तरह ही Tasks की भी एक विशेषता ये होती है कि एक बार जो Task Complete हो जाता है, उसे फिर से Restart नहीं किया जा सकता। यानी किसी Task को Recreate किए बिना उसे फिर से Invoke करने का कोई तरीका नहीं है।

जिस तरह से किसी Thread का एक Unique ID होता है, उसी तरह से हर Task का भी एक Unique ID होता है, जो कि Integer Type का एक Read Only Property होता है, जिसे निम्न Format में Define किया गया है:

public int Id { get; }

जबकि हम Currently Executing Task का ID प्राप्त करने के लिए CurrentID Property को Use कर सकते हैं जो कि एक Read Only Static Property होता है। इसे निम्नानुसार तरीके से Declare किया गया है:

public static Nullable<int> CurrentID { get; }

Using Wait() Method

पिछले Program में Main() Method के Thread को सबसे अन्त में Terminate करने के लिए हमने Thread.Sleep() Method को Use किया है। लेकिन ये कोई Perfect Approach नहीं है। Perfect Approach ये है कि जब तक कोई Task पूरी तरह से Complete न हो, तब तक Main() Thread उस Task के Complete होने का Wait करे और जब Task पूरी तरह से Complete हो जाए, तभी Main Thread Terminate हो। इस जरूरत को पूरा करने के लिए Task Class में Wait() नाम का एक Method Define किया गया है।

परिणामस्वरूप जब हम इस Method को Invoke करते हैं, तो Calling Thread किसी Task के पूरा होने तक Wait करता है और Task के पूरी तरह से Complete हो जाने के बाद ही आगे बढता है। इस Method का Syntax निम्नानुसार होता है:

public void Wait()

ये Method ObjectDisposedException AggregateException नाम के दो Exception Throw कर सकता है। ObjectDisposedException तब Trigger होता है, जबकि किसी Task को Dispose() Method का प्रयोग करके Release कर दिया जाता है। जबकि AggregateException उस समय Trigger होता है, जब कोई Exception Generate होता है या फिर जब Task को Cancel कर दिया जाता है।

सामान्‍यत: हमें इन Exceptions को Monitor करना होता है, क्योंकि यदि किसी Task का कोई Child Task हो, तो वह Task अन्‍य प्रकार के Exceptions भी Generate कर सकता है। इस स्थिति में ये सभी Exceptions मिलकर एक Single AggregateException के रूप में Trigger होते हैं। जहां हम इस AggregateException के Inner Exceptions को Examine करने के लिए अन्‍य Codes लिख सकते हैं और इस बात का पता लगा सकते हैं कि Inner Exceptions क्यों Trigger हुए हैं।

यदि हम हमारे पिछले Program के Main() Method में Use किए गए Thread.Sleep() Method को Wait() Method से Replace करें, तो हमारा Modified Program कुछ निम्नानुसार बनेगा:

File Name: WaitMethod.cs
using System;
using System.Threading;
using System.Threading.Tasks;

namespace CSharpMultiThreading
{
    class DemoTask
    {
        // A method to be run as a task.
        static void MyTask()
        {
            Console.WriteLine("MyTask() starting");
            for (int count = 0; count < 10; count++)
            {
                Thread.Sleep(500);
                Console.WriteLine("In MyTask(), count is " + count);
            }
            Console.WriteLine("MyTask terminating");
        }

        static void Main()
        {
            Console.WriteLine("Main thread starting.");

            // Construct a task.
            Task tsk = new Task(MyTask);

            // Run the task.
            tsk.Start();

            // Keep Main() alive until MyTask() finishes.
            tsk.Wait();
            Console.WriteLine("Main thread ending.");
        }
    }
}

Output:
Main thread starting.
MyTask() starting
In MyTask(), count is 0
In MyTask(), count is 1
In MyTask(), count is 2
In MyTask(), count is 3
In MyTask(), count is 4
In MyTask(), count is 5
In MyTask(), count is 6
In MyTask(), count is 7
In MyTask(), count is 8
In MyTask(), count is 9
MyTask terminating
Main thread ending.

हम देख सकते हैं कि पिछले Program और इस Program दोनों का ही Output एक समान है, जिनमें Main Thread सबसे पहले शुरू होकर सबसे बाद में Terminate होता है और Wait() Method Use करने के कारण Main Thread हमेंशा Secondary Thread के पूरी तरह से Complete होने का Wait करता है।

जब हम एक ही Program में एक से ज्यादा Task Create करते हैं, तो सभी Tasks के लिए अलग-अलग Wait Method को निम्नानुसार तरीके से Call किया जाता है:

tsk1.Wait();
tsk2.Wait();
tsk3.Wait();

लेकिन यदि हम चाहें तो सभी Tasks के लिए WaitAll() नाम के Static Method को Call कर सकते हैं, जो कि Parameter के रूप में Tasks का एक Array Accept करता है। इस Method का Syntax निम्नानुसार होता है:

public static void WaitAll( params Task[] tasks)

जिन Tasks के Complete होने के लिए हमें Wait करना होता है, उन्हें हम इस WaitAll() Method में निम्नानुसार तरीके से Parameter की तरह Pass कर सकते हैं:

Task.WaitAll( tsk1, tsk2, tsk3)

चूंकि WaitAll() Method Parameter के रूप में params के माध्‍यम से Variable Number of Arguments को Support करता है, इसीलिए हम उपरोक्तानुसार तरीके से WaitAll() Method को Use कर सकते हैं।

यदि दो अलग Tasks एक दूसरे के Complete होने का लगातार Wait करते रहे, तो इस स्थिति को Deadlock की स्थिति कहा जाता है और हमें हमेंशा ऐसी स्थिति को Avoid करना होता है। इसलिए यदि WaitAll() Method से Control Return नहीं होताए तो ये इसी बात की सम्भावना है कि दो या अधिक Tasks एक दूसरे के Complete होने का Wait कर रहे हैं। यानी Deadlock की स्थिति मे हैं। यही बात Wait() Method पर भी समान रूप से Apply होती हैं

कई बार हम किसी Group of Tasks के Complete होने का Wait करना चाहते हैं। इस जरूरत को पूरा करने के लिए हम WaitAny() Method को Use कर सकते हैं, जिसे निम्नानुसार Define किया गया है:

public static int WaitAny( params Task[] tasks)

जिन Tasks के Complete होने के लिए हम Wait करना चाहते हैं, उन्हें हम इस Method में Parameters की तरह Pass करते हैं। Tasks को एक Task Array Objects के Array की तरह भी Pass किया जा सकता है और ये Method उस Task का Index Number Return करता है, जो पहले Complete हो जाता है। इस Method को Use करने पर विभिन्न प्रकार के Exceptions Generate हो सकते हैं और इसे निम्नानुसार तरीके से Use किया जा सकता है:

Task.WaitAny( tsk1, tsk2, tsk3)

जब हम Wait(), WaitAll() या WaitAny() में से किसी Method को Use करते हैं, तो जैसे ही Task Complete होता है, Main() Automatically Resume हो जाता है, जो कि एक अच्छा Approach है बजाए इसके कि Main Thread बार-बार इस बात को Check करके कि Secondary Thread Complete हुआ या नहीं।

इन Methods के अलावा इनके कई अन्‍य Versions भी Define किए गए है, जिन्हें Use करते हुए हम Timeout समय भी Specify कर सकते हैं। इन विभिन्न Wait() Method Versions के लिए हम Visual Studio के Object Browser Utility को उपयोग में ले सकते हैं।

Calling Dispose() Method

Task Class IDisposable Interface को Implement करता है, जिसमें Dispose() Method को Declare किया ग; है। ये Method किसी Task द्वारा Use किए जाने वाले Resources को Dispose यानी Release करता है।

सामान्‍यत: किसी Task के साथ Associated Resources उस समय Garbage Collector द्वारा Automatically Collect कर लिया जाता है, जब Task Complete होता है। फिर भी यदि हम किसी Task को किसी विशेष Situation में Complete होने से पहले ही Terminate करना चाहें, तो हम Dispose() Method को Use कर सकते हैं।

ध्‍यान देने वाली बात ये है कि Dispose() Method को किसी भी Task के साथ उसी समय Call किया जा सकता है जब वह Task Complete हो चुका हो। अत: हमें Dispose() Method को Call करने से पहले Wait() जैसे किसी Method को Use करके एक Mechanism तैयार करना होता है, ताकि हम इस बात का पता लगा सकें कि Dispose() Execute होने से पहले Task Complete हो चुका है या नहीं।

जबकि यदि हम बिना Wait() जैसे किसी Method को Use किए हुए Directly किसी Task को Dispose() Method का प्रयोग करके Dispose करेंगे, तो हमें InvalidOperationException नाम का Exception प्राप्त होगा। इस Method को तभी उपयोग में लिया जाता है, जब हमारे Multi-Threaded Program में बहुत ही ज्यादा Tasks Create किए गए हों अन्‍यथा इस Method का कोई Effect दिखाई नहीं देता।

C# ThreadPool
Task.Factory.StartNew - Task Factory

******

ये पोस्‍ट Useful लगा हो, तो Like कर दीजिए।

C# in Hindiये Article इस वेबसाईट पर Selling हेतु उपलब्‍ध EBook C#.NET in Hindi से लिया गया है। इसलिए यदि ये Article आपके लिए उपयोगी रहा, तो निश्चित रूप से ये पुस्तक भी आपके लिए काफी उपयोगी साबित होगी। 

C#.NET in Hindi | Page:908 | Format: PDF

BUY NOW DOWNLOAD READ ONLINE

Download All Hindi EBooks

सभी हिन्दी EBooks C, C++, Java, C#, ASP.NET, Oracle, Data Structure, VB6, PHP, HTML5, JavaScript, jQuery, WordPress, etc... के DOWNLOAD LINKS प्राप्‍त करें, अपने EMail पर।

Register करके Login करें। इस Popup से छुटकारा पाएें।