खोज…


टिप्पणियों

एक धागा एक प्रोग्राम का एक हिस्सा है जो अन्य भागों के स्वतंत्र रूप से निष्पादित कर सकता है। यह अन्य थ्रेड्स के साथ एक साथ कार्य कर सकता है। मल्टीथ्रेडिंग एक ऐसी सुविधा है जो कार्यक्रमों को समवर्ती प्रसंस्करण करने में सक्षम बनाती है ताकि एक बार में एक से अधिक ऑपरेशन किए जा सकें।

उदाहरण के लिए, आप अग्रभूमि में अन्य कार्यों को एक साथ करते समय पृष्ठभूमि में एक टाइमर या काउंटर को अपडेट करने के लिए थ्रेडिंग का उपयोग कर सकते हैं।

मल्टीट्र्रेड किए गए एप्लिकेशन उपयोगकर्ता इनपुट के लिए अधिक उत्तरदायी हैं और आसानी से स्केलेबल भी हैं, क्योंकि डेवलपर थ्रेड जोड़ सकते हैं जब और जब कार्यभार बढ़ता है।

डिफ़ॉल्ट रूप से, एक C # प्रोग्राम में एक थ्रेड होता है - मुख्य प्रोग्राम थ्रेड। हालाँकि, प्राथमिक थ्रेड के साथ समानांतर में कोड निष्पादित करने के लिए द्वितीयक थ्रेड बनाए और उपयोग किए जा सकते हैं। ऐसे धागों को श्रमिक सूत्र कहा जाता है।

एक थ्रेड के संचालन को नियंत्रित करने के लिए, CLR एक फ़ंक्शन को थ्रेड शेड्यूलर के रूप में जाना जाता है। एक थ्रेड शेड्यूलर आश्वासन देता है कि सभी थ्रेड्स को उचित निष्पादन समय आवंटित किया गया है। यह भी जांचता है कि जो धागे अवरुद्ध या बंद हैं, वे सीपीयू समय का अधिक उपभोग नहीं करते हैं।

.NET फ्रेमवर्क System.ThreadingSystem.Threading नेमस्पेस थ्रेड का उपयोग करना आसान बनाता है। System.Threading कई वर्गों और इंटरफेस प्रदान करके मल्टीथ्रेडिंग को सक्षम बनाता है। किसी विशेष धागे के लिए प्रकार और कक्षाएं प्रदान करने के अलावा, यह थ्रेड्स, टाइमर क्लास और इतने पर का संग्रह रखने के लिए प्रकारों को भी परिभाषित करता है। यह साझा डेटा तक सिंक्रनाइज़ एक्सेस की अनुमति देकर अपना समर्थन भी प्रदान करता है।

Thread System.Threading नाम स्थान में मुख्य वर्ग है। अन्य वर्गों में AutoResetEvent , Interlocked , Monitor , Mutex और ThreadPool

प्रतिनिधियों कि में मौजूद हैं में से कुछ System.Threading नेमस्पेस शामिल ThreadStart , TimerCallback , और WaitCallback

में Enumerations System.Threading नेमस्पेस शामिल ThreadPriority , ThreadState , और EventResetMode

.NET फ्रेमवर्क 4 और बाद के संस्करणों में, बहु प्रोग्रामिंग के माध्यम से आसान और सरल बना है System.Threading.Tasks.Parallel और System.Threading.Tasks.Task कक्षाएं, समानांतर LINQ (PLINQ), में नए समवर्ती संग्रह कक्षाएं System.Collections.Concurrent नामस्थान, और एक नया कार्य-आधारित प्रोग्रामिंग मॉडल।

सरल पूर्ण थ्रेडिंग डेमो

class Program
{
    static void Main(string[] args)
    {
        // Create 2 thread objects.  We're using delegates because we need to pass 
        // parameters to the threads.  
        var thread1 = new Thread(new ThreadStart(() => PerformAction(1)));
        var thread2 = new Thread(new ThreadStart(() => PerformAction(2)));

        // Start the threads running 
        thread1.Start();
        // NB: as soon as the above line kicks off the thread, the next line starts; 
        // even if thread1 is still processing.
        thread2.Start();

        // Wait for thread1 to complete before continuing
        thread1.Join();
        // Wait for thread2 to complete before continuing
        thread2.Join();

        Console.WriteLine("Done");
        Console.ReadKey();
    }

    // Simple method to help demonstrate the threads running in parallel.
    static void PerformAction(int id)
    {
        var rnd = new Random(id);
        for (int i = 0; i < 100; i++)
        {
            Console.WriteLine("Thread: {0}: {1}", id, i);
            Thread.Sleep(rnd.Next(0, 1000));
        }
    }
}

कार्य का उपयोग करते हुए सरल पूर्ण थ्रेडिंग डेमो

class Program
{
    static void Main(string[] args)
    {
        // Run 2 Tasks.  
        var task1 = Task.Run(() => PerformAction(1)));
        var task2 = Task.Run(() => PerformAction(2)));

        // Wait (i.e. block this thread) until both Tasks are complete.
        Task.WaitAll(new [] { task1, task2 });
        
        Console.WriteLine("Done");
        Console.ReadKey();
    }

    // Simple method to help demonstrate the threads running in parallel.
    static void PerformAction(int id)
    {
        var rnd = new Random(id);
        for (int i = 0; i < 100; i++)
        {
            Console.WriteLine("Task: {0}: {1}", id, i);
            Thread.Sleep(rnd.Next(0, 1000));
        }
    }
}

स्पष्ट कार्य समानता

    private static void explicitTaskParallism()
    {
        Thread.CurrentThread.Name = "Main";

        // Create a task and supply a user delegate by using a lambda expression. 
        Task taskA = new Task(() => Console.WriteLine($"Hello from task {nameof(taskA)}."));
        Task taskB = new Task(() => Console.WriteLine($"Hello from task {nameof(taskB)}."));

        // Start the task.
        taskA.Start();
        taskB.Start();

        // Output a message from the calling thread.
        Console.WriteLine("Hello from thread '{0}'.",
                          Thread.CurrentThread.Name);
        taskA.Wait();
        taskB.Wait();
        Console.Read();
    }

निहित कार्य समानता

    private static void Main(string[] args)
    {
        var a = new A();
        var b = new B();
        //implicit task parallelism
        Parallel.Invoke(
            () => a.DoSomeWork(),
            () => b.DoSomeOtherWork()
            );

      }

दूसरा थ्रेड बनाना और शुरू करना

यदि आप कई लंबी गणनाएं कर रहे हैं, तो आप उन्हें एक ही समय में अपने कंप्यूटर पर अलग-अलग थ्रेड पर चला सकते हैं। ऐसा करने के लिए, हम एक नया थ्रेड बनाते हैं और यह एक अलग विधि की ओर इशारा करता है।

using System.Threading;

class MainClass {
    static void Main() {
        var thread = new Thread(Secondary);
        thread.Start();
    }

    static void Secondary() {
        System.Console.WriteLine("Hello World!");
    }
}

मापदंडों के साथ एक धागा शुरू करना

System.Threading का उपयोग करना;

class MainClass {
    static void Main() {
        var thread = new Thread(Secondary);
        thread.Start("SecondThread");
    }

    static void Secondary(object threadName) {
        System.Console.WriteLine("Hello World from thread: " + threadName);
    }
}

प्रति प्रोसेसर एक धागा बनाना

Environment.ProcessorCount वर्तमान मशीन पर तार्किक प्रोसेसर की संख्या प्राप्त करता है।

सीएलआर तब प्रत्येक थ्रेड को लॉजिकल प्रोसेसर में शेड्यूल करेगा, यह सैद्धांतिक रूप से प्रत्येक थ्रेड को एक अलग लॉजिकल प्रोसेसर पर, सभी थ्रेड्स को एक लॉजिकल प्रोसेसर या किसी अन्य संयोजन पर सेट कर सकता है।

using System;
using System.Threading;

class MainClass {
    static void Main() {
        for (int i = 0; i < Environment.ProcessorCount; i++) {
            var thread = new Thread(Secondary);
            thread.Start(i);
        }
        
    }

    static void Secondary(object threadNumber) {
        System.Console.WriteLine("Hello World from thread: " + threadNumber);
    }
}

डेटा को पढ़ने और लिखने से बचना

कभी-कभी, आप चाहते हैं कि आपके धागे एक साथ डेटा साझा करें। जब ऐसा होता है तो कोड के बारे में पता होना और गलत होने वाले किसी भी हिस्से को लॉक करना महत्वपूर्ण है। दो धागे की गिनती का एक सरल उदाहरण नीचे दिखाया गया है।

यहाँ कुछ खतरनाक (गलत) कोड है:

using System.Threading;

class MainClass 
{    
    static int count { get; set; }

    static void Main() 
    {
        for (int i = 1; i <= 2; i++)
        {
            var thread = new Thread(ThreadMethod);
            thread.Start(i);
            Thread.Sleep(500);
        }
    }

    static void ThreadMethod(object threadNumber) 
    {
        while (true)
        {
            var temp = count;
            System.Console.WriteLine("Thread " + threadNumber + ": Reading the value of count.");
            Thread.Sleep(1000);
            count = temp + 1;
            System.Console.WriteLine("Thread " + threadNumber + ": Incrementing the value of count to:" + count);
            Thread.Sleep(1000);
        }
    }
}

आप देखेंगे, 1,2,3,4,5 की गिनती के बजाय ... हम 1,1,2,2,3 गिनते हैं ...

इस समस्या को ठीक करने के लिए, हमें गणना के मूल्य को लॉक करने की आवश्यकता है, ताकि एक ही समय में कई अलग-अलग थ्रेड्स इसे पढ़ और लिख न सकें। एक लॉक और एक कुंजी को जोड़ने के साथ, हम थ्रेड्स को एक साथ डेटा तक पहुंचने से रोक सकते हैं।

using System.Threading;

class MainClass
{

    static int count { get; set; } 
    static readonly object key = new object();

    static void Main()
    {
        for (int i = 1; i <= 2; i++)
        {
            var thread = new Thread(ThreadMethod);
            thread.Start(i);
            Thread.Sleep(500);
        }
    }

    static void ThreadMethod(object threadNumber)
    {
        while (true)
        {
            lock (key) 
            {
                var temp = count;
                System.Console.WriteLine("Thread " + threadNumber + ": Reading the value of count.");
                Thread.Sleep(1000);
                count = temp + 1;
                System.Console.WriteLine("Thread " + threadNumber + ": Incrementing the value of count to:" + count);
            }
            Thread.Sleep(1000);
        }
    }
}

समानांतर। लॉरच लूप

यदि आपके पास एक फ़ॉरच लूप है जिसे आप गति देना चाहते हैं और आपको इस बात से कोई आपत्ति नहीं है कि आउटपुट किस क्रम में है, तो आप इसे निम्न के द्वारा एक समानांतर फ़ॉरेस्ट लूप में बदल सकते हैं:

using System;
using System.Threading;
using System.Threading.Tasks;

public class MainClass {

    public static void Main() {
        int[] Numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        // Single-threaded
        Console.WriteLine("Normal foreach loop: ");
        foreach (var number in Numbers) {
            Console.WriteLine(longCalculation(number));
        }
        // This is the Parallel (Multi-threaded solution)
        Console.WriteLine("Parallel foreach loop: ");
        Parallel.ForEach(Numbers, number => {
            Console.WriteLine(longCalculation(number));
        });
    }

    private static int longCalculation(int number) {
        Thread.Sleep(1000); // Sleep to simulate a long calculation
        return number * number;
    }
}

डेडलॉक (प्रत्येक सूत्र पर प्रतीक्षा कर रहे दो धागे)

एक गतिरोध वह होता है जब दो या दो से अधिक धागे प्रत्येक अभिभावक के पूरा होने या इस तरह से संसाधन जारी करने की प्रतीक्षा कर रहे होते हैं कि वे हमेशा के लिए प्रतीक्षा करें।

प्रत्येक थ्रेड को पूरा करने के लिए प्रतीक्षा कर रहे दो थ्रेड्स का एक विशिष्ट परिदृश्य तब होता है जब एक Windows प्रपत्र GUI थ्रेड किसी श्रमिक थ्रेड के लिए प्रतीक्षा करता है और वर्कर थ्रेड GUI थ्रेड द्वारा प्रबंधित ऑब्जेक्ट को लागू करने का प्रयास करता है। निरीक्षण करें कि इस कोड के साथ, बटन 1 पर क्लिक करने से प्रोग्राम लटक जाएगा।

private void button1_Click(object sender, EventArgs e)
{
    Thread workerthread= new Thread(dowork);
    workerthread.Start();
    workerthread.Join();
    // Do something after
}

private void dowork()
{
    // Do something before
    textBox1.Invoke(new Action(() => textBox1.Text = "Some Text"));
    // Do something after
}

workerthread.Join() एक कॉल है जो कॉलिंग थ्रेड को ब्लॉक करता है जब तक कि वर्थथ्रेड पूरा नहीं हो जाता। textBox1.Invoke(invoke_delegate) एक कॉल है जो कॉलिंग थ्रेड को ब्लॉक करती है जब तक GUI थ्रेड ने invoke_delegate को संसाधित नहीं किया है, लेकिन यह कॉल डेडलॉक का कारण बनता है यदि GUI थ्रेड पहले से ही कॉलिंग थ्रेड के पूरा होने का इंतजार कर रहा है।

इसके आस-पास जाने के लिए, व्यक्ति टेक्स्ट बॉक्स के बजाय एक गैर-अवरुद्ध तरीके का उपयोग कर सकता है:

private void dowork()
{
    // Do work
    textBox1.BeginInvoke(new Action(() => textBox1.Text = "Some Text"));
    // Do work that is not dependent on textBox1 being updated first
}

हालाँकि, इससे आपको परेशानी होगी यदि आपको उस कोड को चलाने की आवश्यकता है जो पहले अपडेट किए जा रहे टेक्स्टबॉक्स पर निर्भर है। उस स्थिति में, इसे आह्वान के हिस्से के रूप में चलाएं, लेकिन ध्यान रखें कि यह इसे GUI थ्रेड पर चलाएगा।

private void dowork()
{
    // Do work
    textBox1.BeginInvoke(new Action(() => {
        textBox1.Text = "Some Text";
        // Do work dependent on textBox1 being updated first, 
        // start another worker thread or raise an event
    }));
    // Do work that is not dependent on textBox1 being updated first
}

वैकल्पिक रूप से पूरे नए थ्रेड को शुरू करें और ऐसा करें कि कोई GUI थ्रेड पर प्रतीक्षा कर रहा है, ताकि वर्कशीट पूरी हो जाए।

private void dowork()
{
    // Do work
    Thread workerthread2 = new Thread(() =>
    {
        textBox1.Invoke(new Action(() => textBox1.Text = "Some Text"));
        // Do work dependent on textBox1 being updated first, 
        // start another worker thread or raise an event
    });
    workerthread2.Start();
    // Do work that is not dependent on textBox1 being updated first
}

पारस्परिक प्रतीक्षा के गतिरोध में चलने के जोखिम को कम करने के लिए, हमेशा संभव होने पर थ्रेड्स के बीच परिपत्र संदर्भों से बचें। थ्रेड्स का एक पदानुक्रम जहां निचली रैंकिंग वाले थ्रेड केवल उच्च रैंकिंग वाले थ्रेड्स के लिए संदेश छोड़ते हैं और उन पर कभी प्रतीक्षा नहीं करते हैं, इस तरह के मुद्दे में नहीं चलेंगे। हालाँकि, यह संसाधन लॉकिंग के आधार पर गतिरोध के लिए अभी भी असुरक्षित होगा।

डेडलॉक (संसाधन पकड़ें और प्रतीक्षा करें)

एक गतिरोध वह होता है जब दो या दो से अधिक धागे प्रत्येक अभिभावक के पूरा होने या इस तरह से संसाधन जारी करने की प्रतीक्षा कर रहे होते हैं कि वे हमेशा के लिए प्रतीक्षा करें।

यदि थ्रेड 1 संसाधन ए पर लॉक रखता है और संसाधन बी के जारी होने की प्रतीक्षा कर रहा है, जबकि थ्रेड 2 संसाधन बी रखता है और संसाधन ए के जारी होने की प्रतीक्षा कर रहा है, तो वे गतिरोध हैं।

निम्नलिखित उदाहरण कोड के लिए बटन 1 पर क्लिक करने से आपका आवेदन पूर्वोक्त गतिरोध की स्थिति में आ जाएगा और लटक जाएगा

private void button_Click(object sender, EventArgs e)
{
    DeadlockWorkers workers = new DeadlockWorkers();
    workers.StartThreads();
    textBox.Text = workers.GetResult();
}

private class DeadlockWorkers
{
    Thread thread1, thread2;

    object resourceA = new object();
    object resourceB = new object();

    string output;

    public void StartThreads()
    {
        thread1 = new Thread(Thread1DoWork);
        thread2 = new Thread(Thread2DoWork);
        thread1.Start();
        thread2.Start();
    }

    public string GetResult()
    {
        thread1.Join();
        thread2.Join();
        return output;
    }

    public void Thread1DoWork()
    {
        Thread.Sleep(100);
        lock (resourceA)
        {
            Thread.Sleep(100);
            lock (resourceB)
            {
                output += "T1#";
            }
        }
    }

    public void Thread2DoWork()
    {
        Thread.Sleep(100);
        lock (resourceB)
        {
            Thread.Sleep(100);
            lock (resourceA)
            {
                output += "T2#";
            }
        }
    }
}

इस तरह से डेडलॉक होने से बचने के लिए, यदि कोई लॉक पहले से ही एक ऑब्जेक्ट पर रखा गया है, तो यह देखने के लिए कि कोई व्यक्ति मॉनिटर। ट्राइटर (Lock_object, timeout_in_milliseconds) का उपयोग कर सकता है। यदि Monitor.TryEnter टाइमआउट_in_milliseconds से पहले lock_object पर एक लॉक प्राप्त करने में सफल नहीं होता है, तो यह गलत है, थ्रेड को अन्य आयोजित संसाधनों और उपज देने का मौका देता है, इस प्रकार अन्य थ्रेड्स को ऊपर के इस थोड़ा संशोधित संस्करण में पूरा करने का मौका देता है। :

private void button_Click(object sender, EventArgs e)
{
    MonitorWorkers workers = new MonitorWorkers();
    workers.StartThreads();
    textBox.Text = workers.GetResult();
}

private class MonitorWorkers
{
    Thread thread1, thread2;

    object resourceA = new object();
    object resourceB = new object();

    string output;

    public void StartThreads()
    {
        thread1 = new Thread(Thread1DoWork);
        thread2 = new Thread(Thread2DoWork);
        thread1.Start();
        thread2.Start();
    }

    public string GetResult()
    {
        thread1.Join();
        thread2.Join();
        return output;
    }

    public void Thread1DoWork()
    {
        bool mustDoWork = true;
        Thread.Sleep(100);
        while (mustDoWork)
        {
            lock (resourceA)
            {
                Thread.Sleep(100);
                if (Monitor.TryEnter(resourceB, 0))
                {
                    output += "T1#";
                    mustDoWork = false;
                    Monitor.Exit(resourceB);
                }
            }
            if (mustDoWork) Thread.Yield();
        }
    }

    public void Thread2DoWork()
    {
        Thread.Sleep(100);
        lock (resourceB)
        {
            Thread.Sleep(100);
            lock (resourceA)
            {
                output += "T2#";
            }
        }
    }
}

ध्यान दें कि यह समाधान थ्रेड 2 पर निर्भर करता है, इसके ताले और थ्रेड 1 के बारे में जिद्दी होने के लिए तैयार हैं, जैसे कि थ्रेड 2 हमेशा पूर्वता लेता है। यह भी ध्यान दें कि थ्रेड 1 को संसाधन ए को लॉक करने के बाद किए गए कार्य को फिर से करना होगा, जब यह पैदावार देता है। इसलिए एक से अधिक पैदावार करने वाले धागे के साथ इस दृष्टिकोण को लागू करते समय सावधान रहें, जैसा कि आप तब तथाकथित लाइवलॉक में प्रवेश करने का जोखिम उठाएंगे - एक ऐसी स्थिति जो तब होती है जब दो धागे अपने काम का पहला बिट करते रहे और फिर पारस्परिक रूप से उपज करते हैं बार-बार शुरू हो रहा है।



Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow