2009-12-16 26 views
31

मैं एक दृश्य सी # प्रोग्राम लिख रहा हूं जो माध्यमिक धागे पर संचालन के निरंतर लूप को निष्पादित करता है। कभी-कभी जब वह धागा एक कार्य पूरा करता है तो मैं इसे एक ईवेंटशेलर ट्रिगर करना चाहता हूं। मेरा प्रोग्राम ऐसा करता है लेकिन जब ईवेंट हैंडलर ट्रिगर होता है, तब तक द्वितीयक थ्रेड प्रतीक्षा करता है जब तक कि थ्रेड जारी रखने से पहले ईवेंट हैंडलर समाप्त हो जाता है। मैं इसे कैसे जारी रखूं? यहां जिस तरीके से मैंने वर्तमान में इसे संरचित किया है ...मैं इवेंटहालर को असीमित रूप से कैसे चला सकता हूं?

class TestClass 
{ 
    private Thread SecondaryThread; 
    public event EventHandler OperationFinished; 

    public void StartMethod() 
    { 
    ... 
    SecondaryThread.Start();  //start the secondary thread 
    } 

    private void SecondaryThreadMethod() 
    { 
    ... 
    OperationFinished(null, new EventArgs()); 
    ... //This is where the program waits for whatever operations take 
     //place when OperationFinished is triggered. 
    } 

} 

यह कोड मेरे डिवाइस में से किसी एक के लिए एक एपीआई का हिस्सा है। जब ऑपरेशनफिनिश इवेंट ट्रिगर किया जाता है, तो मैं चाहता हूं कि क्लाइंट एप्लिकेशन एपीआई ऑपरेशन को रोक दिए बिना जो कुछ भी करने की आवश्यकता हो (यानी तदनुसार जीयूआई अपडेट करें) कर सके।

इसके अलावा, अगर मैं ईवेंट हैंडलर को किसी भी पैरामीटर को पास नहीं करना चाहता हूं तो मेरा वाक्यविन्यास OperationFinished(null, new EventArgs()) का उपयोग कर सही है?

+0

कौन सा धागा आप चाहते हैं 'OperationFinished' घटना पर उठाया जा सकता हूँ? यह आपका माध्यमिक धागा नहीं हो सकता है, क्योंकि आपको स्पष्ट रूप से इसे अवरुद्ध करने की आवश्यकता नहीं है। क्या इसे प्राथमिक धागा होना चाहिए, या फिर आप इसे एसिंक कॉलबैक के उद्देश्य के लिए बनाए गए एक अलग धागे पर उठाए जाने के साथ ठीक हैं? –

उत्तर

44

तो आप इस घटना को इस तरह से उठाना चाहते हैं जो श्रोताओं को पृष्ठभूमि धागे को अवरुद्ध करने से रोकता है? एक उदाहरण चाबुक करने के लिए कुछ मिनट दें; यह बहुत आसान है :-)

यहां हम जाते हैं: पहले एक महत्वपूर्ण नोट! जब भी आप BeginInvoke पर कॉल करते हैं तो आपको संबंधित EndInvoke पर कॉल करना होगा, अन्यथा यदि लागू विधि ने अपवाद फेंक दिया या एक मान लौटाया तो थ्रेडपूल थ्रेड पूल पर वापस कभी जारी नहीं किया जाएगा, जिसके परिणामस्वरूप थ्रेड-रिसाव होगा!

class TestHarness 
{ 

    static void Main(string[] args) 
    { 
     var raiser = new SomeClass(); 

     // Emulate some event listeners 
     raiser.SomeEvent += (sender, e) => { Console.WriteLine(" Received event"); }; 
     raiser.SomeEvent += (sender, e) => 
     { 
      // Bad listener! 
      Console.WriteLine(" Blocking event"); 
      System.Threading.Thread.Sleep(5000); 
      Console.WriteLine(" Finished blocking event"); 
     }; 

     // Listener who throws an exception 
     raiser.SomeEvent += (sender, e) => 
     { 
      Console.WriteLine(" Received event, time to die!"); 
      throw new Exception(); 
     }; 

     // Raise the event, see the effects 
     raiser.DoSomething(); 

     Console.ReadLine(); 
    } 
} 

class SomeClass 
{ 
    public event EventHandler SomeEvent; 

    public void DoSomething() 
    { 
     OnSomeEvent(); 
    } 

    private void OnSomeEvent() 
    { 
     if (SomeEvent != null) 
     { 
      var eventListeners = SomeEvent.GetInvocationList(); 

      Console.WriteLine("Raising Event"); 
      for (int index = 0; index < eventListeners.Count(); index++) 
      { 
       var methodToInvoke = (EventHandler)eventListeners[index]; 
       methodToInvoke.BeginInvoke(this, EventArgs.Empty, EndAsyncEvent, null); 
      } 
      Console.WriteLine("Done Raising Event"); 
     } 
    } 

    private void EndAsyncEvent(IAsyncResult iar) 
    { 
     var ar = (System.Runtime.Remoting.Messaging.AsyncResult)iar; 
     var invokedMethod = (EventHandler)ar.AsyncDelegate; 

     try 
     { 
      invokedMethod.EndInvoke(iar); 
     } 
     catch 
     { 
      // Handle any exceptions that were thrown by the invoked method 
      Console.WriteLine("An event listener went kaboom!"); 
     } 
    } 
} 
+2

GetInvocationList का उपयोग करने के बजाय, मल्टीकास्ट प्रतिनिधि को सीधे क्यों न बुलाएं? – thecoop

+1

आप इवेंट श्रोताओं को अतुल्यकालिक रूप से इसका उपयोग कैसे करेंगे? अनुमोदित, आप * सभी * श्रोताओं को एक अलग एकल थ्रेड पर कॉल कर सकते हैं - मेरा समाधान इसे * प्रत्येक * श्रोता को अपने स्वयं के धागे पर कॉल करने के स्तर पर ले जाता है - इसलिए मैं इसे ओवरकिल देख सकता था। – STW

+0

जिस तरह से मैंने मूल रूप से लिखा था, अगर क्लाइंट ऐप (कोई श्रोताओं) में ईवेंट को संभालने का कोई तरीका नहीं था तो क्लाइंट ऐप अपवाद फेंक देगा। क्या आप इसे लूप के लिए उपयोग करके ऐसा करने से रोकते हैं जो घटनासूची के माध्यम से loops? – PICyourBrain

0

BackgroundWorker कक्षा को देखें। मुझे लगता है कि यह वही करता है जो आप पूछ रहे हैं।

संपादित करें: मुझे लगता है कि आप क्या पूछ रहे हैं कि एक घटना को कैसे आग लगाना है जब समग्र पृष्ठभूमि कार्य का केवल एक छोटा सा हिस्सा पूरा हो गया है। BackgroundWorker "प्रोग्रेस चेंजेड" नामक एक ईवेंट प्रदान करता है जो आपको मुख्य थ्रेड पर रिपोर्ट करने की अनुमति देता है कि समग्र प्रक्रिया का कुछ हिस्सा पूरा हो गया है। फिर, जब सभी async काम पूरा हो जाता है, तो यह "RunWorkerCompleted" ईवेंट उठाता है।

+1

यह सुनिश्चित नहीं है कि पृष्ठभूमि कार्यकर्ता इस स्थिति में कैसे मदद करता है। अनुमोदित, जब आपको सूचनाओं की आवश्यकता होती है तो काम को एक अलग थ्रेड में धक्का देने के लिए यह एक अच्छा विकल्प है, लेकिन इस मामले में, हैंडलर को एक अलग थ्रेड में धक्का देने के लिए यह एक साधारण काम वस्तु है ... –

+0

यदि मैं क्लाइंट एप्लिकेशन लिख रहा था, तो मैं उस पद्धति में हो सकता है जो जीयूआई को बैकग्राउंडर में चलाता है और जो कॉलिंग को ऑपरेशनफिनिश() को अवरुद्ध करने से रोक देगा, लेकिन जैसा कि मैं क्लाइंट ऐप नहीं लिख रहा हूं, मैं ऐसा नहीं कर सकता। क्या आप कह रहे हैं कि OpeartionFinished() को मेरी कॉल पृष्ठभूमिवर्कर के भीतर होनी चाहिए? – PICyourBrain

11

इसके अलावा, अगर मैं ईवेंट हैंडलर को किसी भी पैरामीटर को पास नहीं करना चाहता हूं तो मेरा वाक्यविन्यास ऑपरेशनफिनिश (शून्य, नया EventArgs()) का उपयोग कर सही है?

नहीं। आमतौर पर, आप इसे के रूप में कहेंगे:

OperationFinished(this, EventArgs.Empty); 

तुम हमेशा एक प्रेषक के रूप में एक वस्तु पारित चाहिए - यह पैटर्न में होने की उम्मीद है (हालांकि आम तौर पर नजरअंदाज कर दिया)। EventArgs.Empty नए EventArgs() से भी बेहतर है।

private void RaiseOperationFinished() 
{ 
     ThreadPool.QueueUserWorkItem(new WaitCallback((s) => 
      { 
       if (this.OperationFinished != null) 
        this.OperationFinished(this, EventArgs.Empty); 
      })); 
} 

कहा जा रहा है, एक अलग थ्रेड पर एक घटना को ऊपर उठाने के कुछ है कि अच्छी तरह से होना चाहिए:

एक अलग थ्रेड में यह आग करने के लिए, सबसे आसान विकल्प शायद सिर्फ थ्रेड पूल का उपयोग करने के लिए है दस्तावेज, क्योंकि यह संभावित रूप से अप्रत्याशित व्यवहार का कारण बन जाएगा।

+2

आज, मैं थ्रेड पूल के बजाय 'टास्क.रुन 'का उपयोग करूंगा। – beruic

+1

@beruic सहमत। यह 200 9 में लिखा गया था;) –

6

ईवेंट प्रतिनिधि पर BeginInvoke और EndInvoke विधियों का प्रयास करें - ये तुरंत लौटते हैं, और विधि पूर्ण होने पर आपको सूचित करने के लिए मतदान, प्रतीक्षा प्रतीक्षा या कॉलबैक फ़ंक्शन का उपयोग करने की अनुमति देते हैं। एक सिंहावलोकन के लिए here देखें; आपके उदाहरण में, घटना प्रतिनिधि है जिसका उपयोग आप

0

मैं एक विधि को परिभाषित करना पसंद करता हूं जिसे मैं बच्चे धागे को एक प्रतिनिधि के रूप में पास करता हूं जो यूआई अपडेट करता है। परिभाषित एक प्रतिनिधि सदस्य

public delegate void ChildCallBackDelegate(); 

बच्चे धागा में:

public ChildCallbackDelegate ChildCallback {get; set;} 

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

private void ChildThreadUpdater() 
{ 
    yourControl.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Background 
    , new System.Threading.ThreadStart(delegate 
     { 
     // update your control here 
     } 
    )); 
} 

इससे पहले कि आप अपने बच्चे को धागा लॉन्च करते हैं, इसके ChildCallBack गुण सेट:

theChild.ChildCallBack = new ChildCallbackDelegate(ChildThreadUpdater); 

फिर जब बच्चे धागा माता-पिता को अद्यतन करना चाहता है:

ChildCallBack(); 
+0

क्या आप बैक अप लेने के लिए सूत्रों का हवाला देते हैं कि 'EndInvoke()' की आवश्यकता नहीं है? मेरी समझ यह है कि यह सुनिश्चित करने के लिए हमेशा अच्छा अभ्यास होता है कि इसे थ्रेडिंग संसाधन कहा जाता है, विशेष परिस्थितियों में कॉल के बिना जरूरी नहीं है। साथ ही, क्या कोई कारण है कि आप (अपेक्षाकृत) प्रदर्शन करने वाले थ्रेडपूल की बजाय थ्रेडस्टार्ट का उपयोग करने का विकल्प चुनते हैं? अंततः; यह समाधान यूआई को अपडेट करने में संभालता है, लेकिन मुझे नहीं लगता कि ओपी का सवाल उस तक सीमित था - यह घटनाओं को असंकालिक रूप से बढ़ाने के व्यापक मुद्दे को हल नहीं करता है। – STW

+1

जॉन स्कीट ने यह सबसे अच्छा कहा: http://stackoverflow.com/questions/229554/whats-the-difference-between-invoke-and-begininvoke: "ध्यान दें कि विंडोज फॉर्म टीम ने गारंटी दी है कि आप Control.BeginInvoke का उपयोग कर सकते हैं एक "आग और भूल" तरीके से - यानी एंडइनवोक को कभी भी कॉल किए बिना। यह सामान्य रूप से एसिंक कॉलों के बारे में सच नहीं है: आम तौर पर प्रत्येक BeginXXX के पास कॉलबैक में एक समान एंडXXक्स कॉल होना चाहिए। " यह भी ध्यान रखें कि कम से कम WPF के साथ, कोई डिस्पैचर नहीं है। EndInvoke विधि। –

+0

मैंने अपना समाधान यूआई अपडेट किया है क्योंकि ओपी ने यही निर्दिष्ट किया है: "जब ऑपरेशनफिनिश इवेंट ट्रिगर किया जाता है, तो मैं चाहता हूं कि क्लाइंट एप्लिकेशन एपीआई ऑपरेशन को रोक दिए बिना जो कुछ भी करने की ज़रूरत है (यानी उसके अनुसार जीयूआई अपडेट करें)।" –

8

Task Parallel Library के साथ अब निम्न कार्य करना संभव है:

Task.Factory.FromAsync((asyncCallback, @object) => this.OperationFinished.BeginInvoke(this, EventArgs.Empty, asyncCallback, @object), this.OperationFinished.EndInvoke, null); 
+0

बहुत अच्छा काम करता है, टीपीएल की 'FromAsync' विधि को याद दिलाने के लिए धन्यवाद! – NumberFour

+0

चालाक, लेकिन मल्टीकास्ट प्रतिनिधियों के लिए काम नहीं करेगा –

+1

@ फैक्टर मैटिक क्या आप जानते हैं कि मैं कहां पढ़ सकता हूं कि यह उस मामले में क्यों काम नहीं करता है? – piedar

3

शायद Method2 या नीचे Method3 मदद कर सकते हैं :)

public partial class Form1 : Form 
{ 
    private Thread SecondaryThread; 

    public Form1() 
    { 
     InitializeComponent(); 

     OperationFinished += callback1; 
     OperationFinished += callback2; 
     OperationFinished += callback3; 
    } 

    private void Form1_Load(object sender, EventArgs e) 
    { 
     SecondaryThread = new Thread(new ThreadStart(SecondaryThreadMethod)); 
     SecondaryThread.Start(); 
    } 

    private void SecondaryThreadMethod() 
    { 
     Stopwatch sw = new Stopwatch(); 
     sw.Restart(); 

     OnOperationFinished(new MessageEventArg("test1")); 
     OnOperationFinished(new MessageEventArg("test2")); 
     OnOperationFinished(new MessageEventArg("test3")); 
     //This is where the program waits for whatever operations take 
      //place when OperationFinished is triggered. 

     sw.Stop(); 

     Invoke((MethodInvoker)delegate 
     { 
      richTextBox1.Text += "Time taken (ms): " + sw.ElapsedMilliseconds + "\n"; 
     }); 
    } 

    void callback1(object sender, MessageEventArg e) 
    { 
     Thread.Sleep(2000); 
     Invoke((MethodInvoker)delegate 
     { 
      richTextBox1.Text += e.Message + "\n"; 
     }); 
    } 
    void callback2(object sender, MessageEventArg e) 
    { 
     Thread.Sleep(2000); 
     Invoke((MethodInvoker)delegate 
     { 
      richTextBox1.Text += e.Message + "\n"; 
     }); 
    } 

    void callback3(object sender, MessageEventArg e) 
    { 
     Thread.Sleep(2000); 
     Invoke((MethodInvoker)delegate 
     { 
      richTextBox1.Text += e.Message + "\n"; 
     }); 
    } 

    public event EventHandler<MessageEventArg> OperationFinished; 

    protected void OnOperationFinished(MessageEventArg e) 
    { 
     //##### Method1 - Event raised on the same thread ##### 
     //EventHandler<MessageEventArg> handler = OperationFinished; 

     //if (handler != null) 
     //{ 
     // handler(this, e); 
     //} 

     //##### Method2 - Event raised on (the same) separate thread for all listener ##### 
     //EventHandler<MessageEventArg> handler = OperationFinished; 

     //if (handler != null) 
     //{ 
     // Task.Factory.StartNew(() => handler(this, e)); 
     //} 

     //##### Method3 - Event raised on different threads for each listener ##### 
     if (OperationFinished != null) 
     { 
      foreach (EventHandler<MessageEventArg> handler in OperationFinished.GetInvocationList()) 
      { 
       Task.Factory.FromAsync((asyncCallback, @object) => handler.BeginInvoke(this, e, asyncCallback, @object), handler.EndInvoke, null); 
      } 
     } 
    } 
} 

public class MessageEventArg : EventArgs 
{ 
    public string Message { get; set; } 

    public MessageEventArg(string message) 
    { 
     this.Message = message; 
    } 
} 

}

संबंधित मुद्दे

 संबंधित मुद्दे