.NET के साथ समाप्त होने के लिए थ्रेड का इंतजार कैसे करें?


178

मैंने पहले कभी C # में थ्रेडिंग का उपयोग नहीं किया है जहाँ मुझे दो धागे और साथ ही मुख्य UI थ्रेड की आवश्यकता है। मूल रूप से, मेरे पास निम्नलिखित हैं।

public void StartTheActions()
{
  //Starting thread 1....
  Thread t1 = new Thread(new ThreadStart(action1));
  t1.Start();

  // Now, I want for the main thread (which is calling `StartTheActions` method) 
  // to wait for `t1` to finish. I've created an event in `action1` for this. 
  // The I wish `t2` to start...

  Thread t2 = new Thread(new ThreadStart(action2));
  t2.Start();
}

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


4
यदि आपने "... पहले कभी थ्रेडिंग का उपयोग नहीं किया है ..." तो आप कुछ आसान विकल्पों पर विचार करना चाह सकते हैं जैसे * Async () पैटर्न।
Ðаn

4
यदि आप किसी भी रास्ते को खत्म करने के लिए बस 1 धागे का इंतजार कर रहे हैं, तो आप उस पद्धति को केवल सिंक्रोनाइज़ क्यों नहीं कर रहे हैं?
सविश

13
जब आप एक रैखिक फैशन में प्रसंस्करण कर रहे हैं तो थ्रेड्स का उपयोग करने का क्या मतलब है?
जॉन

1
@ जॉन, यह मेरे लिए पूरी तरह से समझ में आता है कि बैकग्राउंड थ्रेड को बंद करने के कई उपयोग हैं जो उपयोगकर्ता काम करते हैं। इसके अलावा, क्या आपका सवाल पहले वाले जैसा नहीं है?
user34660

रोटेम का जवाब , आसान उपयोग के लिए बैकग्राउंडर का उपयोग करना, यह बहुत सरल है।
कामिल

जवाबों:


264

मैं उपलब्ध 5 विकल्प देख सकता हूं:

1. थ्रेड। जॉइन

मिच के जवाब के साथ के रूप में। लेकिन इससे आपका UI थ्रेड ब्लॉक हो जाएगा, हालाँकि आपको अपने लिए एक टाइमआउट मिल जाता है।


2. का उपयोग करें WaitHandle

ManualResetEventजैसा WaitHandleकि जिस्त्र ने सुझाया है।

ध्यान देने वाली एक बात यह है कि यदि आप कई थ्रेड के लिए प्रतीक्षा करना चाहते हैं, WaitHandle.WaitAll()तो डिफ़ॉल्ट रूप से काम नहीं करेगा, क्योंकि इसके लिए एमटीए थ्रेड की आवश्यकता है। आप इसके Main()साथ अपने तरीके को चिह्नित करके प्राप्त कर सकते हैं MTAThread- हालांकि यह आपके संदेश पंप को अवरुद्ध करता है और मैंने जो पढ़ा है, उससे अनुशंसित नहीं है।


3. एक घटना आग

घटनाओं और बहु-थ्रेडिंग के बारे में जॉन स्कीट के इस पृष्ठ को देखें , यह संभव है कि कोई घटना मेरे ifऔर उसके बीच अनसुनी हो सकती है EventName(this,EventArgs.Empty)- यह मेरे साथ पहले भी हो चुका है।

(उम्मीद है कि ये संकलन, मैंने कोशिश नहीं की है)

public class Form1 : Form
{
    int _count;

    void ButtonClick(object sender, EventArgs e)
    {
        ThreadWorker worker = new ThreadWorker();
        worker.ThreadDone += HandleThreadDone;

        Thread thread1 = new Thread(worker.Run);
        thread1.Start();

        _count = 1;
    }

    void HandleThreadDone(object sender, EventArgs e)
    {
        // You should get the idea this is just an example
        if (_count == 1)
        {
            ThreadWorker worker = new ThreadWorker();
            worker.ThreadDone += HandleThreadDone;

            Thread thread2 = new Thread(worker.Run);
            thread2.Start();

            _count++;
        }
    }

    class ThreadWorker
    {
        public event EventHandler ThreadDone;

        public void Run()
        {
            // Do a task

            if (ThreadDone != null)
                ThreadDone(this, EventArgs.Empty);
        }
    }
}

4. एक प्रतिनिधि का उपयोग करें

public class Form1 : Form
{
    int _count;

    void ButtonClick(object sender, EventArgs e)
    {
        ThreadWorker worker = new ThreadWorker();

        Thread thread1 = new Thread(worker.Run);
        thread1.Start(HandleThreadDone);

        _count = 1;
    }

    void HandleThreadDone()
    {
        // As before - just a simple example
        if (_count == 1)
        {
            ThreadWorker worker = new ThreadWorker();

            Thread thread2 = new Thread(worker.Run);
            thread2.Start(HandleThreadDone);

            _count++;
        }
    }

    class ThreadWorker
    {
        // Switch to your favourite Action<T> or Func<T>
        public void Run(object state)
        {
            // Do a task

            Action completeAction = (Action)state;
            completeAction.Invoke();
        }
    }
}

यदि आप _count विधि का उपयोग करते हैं, तो यह एक विचार हो सकता है (सुरक्षित होने के लिए) इसका उपयोग करने के लिए वेतन वृद्धि

Interlocked.Increment(ref _count)

मैं थ्रेड नोटिफिकेशन के लिए प्रतिनिधियों और घटनाओं का उपयोग करने के बीच के अंतर को जानना चाहूंगा, एकमात्र अंतर जो मुझे पता है कि घटनाओं को सिंक्रोनाइज़ कहा जाता है।


5. इसके बजाय इसे एसिंक्रोनस रूप से करें

इस प्रश्न के उत्तर में इस पद्धति के साथ आपके विकल्पों का बहुत स्पष्ट विवरण है।


गलत धागे पर प्रतिनिधि / घटनाएँ

चीजों को करने का घटना / प्रतिनिधि तरीका मतलब होगा कि आपकी घटना हैंडलर विधि थ्रेड 1 / थ्रेड 2 पर है , मुख्य यूआई थ्रेड नहीं है , इसलिए आपको हैंडहेलड्रेड विधियों के शीर्ष पर सही स्विच करने की आवश्यकता होगी:

// Delegate example
if (InvokeRequired)
{
    Invoke(new Action(HandleThreadDone));
    return;
}

61

जोड़ना

t1.Join();    // Wait until thread t1 finishes

आप इसे शुरू करने के बाद, लेकिन यह बहुत जरूरी नहीं होगा क्योंकि यह मुख्य धागे पर चलने के समान ही आवश्यक है!

अगर आप .NET में थ्रेडिंग की समझ हासिल करना चाहते हैं, तो मैं # # ई-बुक में जो अलबहारी के थ्रेडिंग को पढ़ने की अत्यधिक सिफारिश कर सकता हूं ।


4
भले ही Joinशाब्दिक रूप से पूछने वाले को लगता है कि यह मांग कर रहा है, यह सामान्य रूप से बेहद खराब हो सकता है। एक कॉल Joinउस थ्रेड को लटकाएगा जिससे यह किया जा रहा है। अगर ऐसा होता है तो मुख्य GUI धागा है, यह BAD है ! एक उपयोगकर्ता के रूप में मैं सक्रिय रूप से उन अनुप्रयोगों को घृणा करता हूं जो इस तरह से काम करते हैं। तो कृपया इस प्रश्न के सभी अन्य उत्तर देखें और stackoverflow.com/questions/1221374/…
peSHIr

1
मैं मानता हूं कि सामान्य रूप से सम्मिलित () बुरा है। मैंने शायद अपने उत्तर में इतना स्पष्ट नहीं किया।
मिच गेहूं

2
दोस्तों, एक आकार सभी फिट नहीं है । ऐसी स्थितियां हैं, जब किसी को वास्तव में सुनिश्चित करने की आवश्यकता होती है, उस थ्रेड ने अपना काम समाप्त कर लिया है: विचार करें, कि थ्रेड डेटा को संसाधित करता है, जो कि बस बदलने वाले हैं। ऐसे मामले में कृपापूर्वक रद्द करने के लिए धागे को अधिसूचित करने और उसके खत्म होने तक प्रतीक्षा करने (विशेषकर, जब एक कदम बहुत जल्दी संसाधित होता है) IMO पूरी तरह से उचित है। मैं बल्कि यह कहना चाहूंगा, कि Join is evil (C ++ FAQ terms में) है, अर्थात। इसका उपयोग तब तक नहीं किया जाएगा जब तक कि वास्तव में आवश्यक न हो ।
भूत

5
मैं स्पष्ट रूप से बताना चाहता हूं, कि ज्वाइन एक उपकरण है, जो उपयोगी हो सकता है, इस तथ्य के बावजूद, कि इसका बहुत बार दुरुपयोग किया जाता है। कुछ स्थितियां हैं, जब यह बिना किसी अनावश्यक दुष्प्रभाव के काम करेगा (जैसे कि समय की ध्यान देने योग्य राशि के लिए मुख्य जीयूआई स्टाल को रोकना)।
भूत

2
ज्वाइन एक टूल है? मुझे लगता है कि आप इसे एक विधि पाएंगे।
मिच गेहूं 12

32

पिछले दो उत्तर महान हैं, और सरल परिदृश्यों के लिए काम करेंगे। हालाँकि, थ्रेड को सिंक्रनाइज़ करने के अन्य तरीके हैं। निम्नलिखित भी काम करेगा:

public void StartTheActions()
{
    ManualResetEvent syncEvent = new ManualResetEvent(false);

    Thread t1 = new Thread(
        () =>
        {
            // Do some work...
            syncEvent.Set();
        }
    );
    t1.Start();

    Thread t2 = new Thread(
        () =>
        {
            syncEvent.WaitOne();

            // Do some work...
        }
    );
    t2.Start();
}

नियमावली के अनुसार , .NETResetEvent विभिन्न WaitHandle में से एक है। वे सरल लेकिन बहुत सामान्य उपकरण जैसे लॉक () / मॉनीटर, थ्रेड.जॉइन, आदि की तुलना में अधिक समृद्ध थ्रेड सिंक्रोनाइज़ेशन क्षमता प्रदान कर सकते हैं। उनका उपयोग दो से अधिक थ्रेड को सिंक्रनाइज़ करने के लिए भी किया जा सकता है, जिससे जटिल परिदृश्य जैसे 'मास्टर थ्रेड' की अनुमति मिलती है। यह कई 'बच्चे' धागे का समन्वय करता है, कई समवर्ती प्रक्रियाएं जो एक-दूसरे के कई चरणों पर निर्भर होती हैं, जिन्हें सिंक्रनाइज़ किया जा सकता है, आदि।


30

.NET 4 से उपयोग करने पर यह नमूना आपकी मदद कर सकता है:

class Program
{
    static void Main(string[] args)
    {
        Task task1 = Task.Factory.StartNew(() => doStuff());
        Task task2 = Task.Factory.StartNew(() => doStuff());
        Task task3 = Task.Factory.StartNew(() => doStuff());
        Task.WaitAll(task1, task2, task3);
        Console.WriteLine("All threads complete");
    }

    static void doStuff()
    {
        //do stuff here
    }
}

से: https://stackoverflow.com/a/4190969/1676736


8
आपने अपने उत्तर में थ्रेड्स के बारे में कुछ भी उल्लेख नहीं किया है। सवाल थ्रेड्स का है, टास्क का नहीं। दोनो एक जैसे नहीं हैं।
सुमेरे

7
मैं मूल पोस्टर के लिए एक समान प्रश्न (और ज्ञान के स्तर) के साथ आया था और यह उत्तर मेरे लिए बहुत मूल्यवान था - कार्य मैं जो कर रहा हूं उसके लिए अधिक उपयुक्त हैं और अगर मुझे यह उत्तर नहीं मिला तो मैंने लिखा होगा खुद भयानक धागा पूल।
क्रिस राए

1
@ChrisRae, इसलिए यह मूल प्रश्न में एक टिप्पणी होनी चाहिए, इस तरह का उत्तर नहीं।
Jaime Hablutzel

जैसा कि इस विशिष्ट उत्तर के बारे में मुझे लगता है कि यह शायद यहाँ अधिक समझ में आता है।
क्रिस राए

1
जैसा कि @Suamere ने कहा, यह उत्तर पूरी तरह से ओपी प्रश्न से असंबंधित है।
ग्लेन स्लेडेन


4

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


0

इसे इस्तेमाल करे:

List<Thread> myThreads = new List<Thread>();

foreach (Thread curThread in myThreads)
{
    curThread.Start();
}

foreach (Thread curThread in myThreads)
{
    curThread.Join();
}

0

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

while (threadCounter > 0)
{
    Thread.Sleep(500); //Make it pause for half second so that we don’t spin the cpu out of control.
}

मेरे ब्लॉग पर दस्तावेज। http://www.adamthings.com/post/2012/07/11/ensure-threads-have-finished-before-method-continues-in-c/


4
इसे व्यस्त प्रतीक्षा कहा जाता है। हाँ यह काम करता है और कभी-कभी सबसे अच्छा समाधान होता है, लेकिन यदि संभव हो तो आप इससे बचना चाहते हैं क्योंकि यह CPU समय बर्बाद करता है
MobileMon

@MobileMon एक नियम की तुलना में अधिक दिशानिर्देश है। इस मामले में चूंकि लूप सीपीयू के 0.00000001% बर्बाद कर सकता है और ओपी सी # में कोडिंग कर रहा है, इसलिए इसे कुछ और अधिक कुशल बनाने के बजाय इसे पूरी तरह से समय की बर्बादी होगी। अनुकूलन का पहला नियम है - नहीं। पहले उपाय करें।
स्पिक0xff

0

जब मैं चाहता हूं कि UI किसी कार्य को पूरा करने के लिए प्रतीक्षा करते समय अपने प्रदर्शन को अपडेट करने में सक्षम हो, तो मैं थोड़ी देर के लूप का उपयोग करता हूं जो थ्रेड पर IsAlive का परीक्षण करता है:

    Thread t = new Thread(() => someMethod(parameters));
    t.Start();
    while (t.IsAlive)
    {
        Thread.Sleep(500);
        Application.DoEvents();
    }

-1

यहां एक सरल उदाहरण है जो एक ही वर्ग के भीतर एक चलने के लिए समाप्त होने का इंतजार करता है। यह उसी नाम स्थान पर किसी अन्य वर्ग को कॉल भी करता है। मैं "का उपयोग कर" बयानों को शामिल करता हूं ताकि यह एक Winform के रूप में निष्पादित कर सके जब तक आप बटन 1 बनाते हैं।

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;

namespace ClassCrossCall
{

 public partial class Form1 : Form
 {
  int number = 0; // this is an intentional problem, included for demonstration purposes
  public Form1()
  {
   InitializeComponent();
  }
  private void Form1_Load(object sender, EventArgs e)
  {
   button1.Text="Initialized";
  }
  private void button1_Click(object sender, EventArgs e)
  {
   button1.Text="Clicked";
   button1.Refresh();
   Thread.Sleep(400);
   List<Task> taskList = new List<Task>();
   taskList.Add(Task.Factory.StartNew(() => update_thread(2000)));
   taskList.Add(Task.Factory.StartNew(() => update_thread(4000)));
   Task.WaitAll(taskList.ToArray());
   worker.update_button(this,number);
  }
  public void update_thread(int ms)
  {
   // It's important to check the scope of all variables
   number=ms; // this could be either 2000 or 4000.  Race condition.
   Thread.Sleep(ms);
  }
 }

 class worker
 {
  public static void update_button(Form1 form, int number)
  {
   form.button1.Text=$"{number}";
  }
 }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.