2010-01-07 26 views
7

में Async घटक से आग घटना। मैं .NET 2.0 में एक गैर-दृश्य घटक बना रहा हूं। यह घटक एक असीमित सॉकेट (BeginReceive, EndReceive आदि) का उपयोग करता है। रनटाइम द्वारा बनाए गए वर्कर थ्रेड के संदर्भ में असीमित कॉलबैक को कॉल किया जाता है। घटक उपयोगकर्ता को मल्टीथ्रेडिंग के बारे में चिंता करने की ज़रूरत नहीं है (यह मुख्य लक्ष्य है, जो मैं चाहता हूं)यूआई थ्रेड

घटक उपयोगकर्ता किसी भी धागे में मेरा गैर-दृश्य घटक बना सकता है (UI थ्रेड सरल के लिए केवल एक सामान्य धागा है अनुप्रयोगों। अधिक गंभीर अनुप्रयोग एक मनमानी कार्यकर्ता थ्रेड के भीतर घटक बना सकते हैं)। घटक ट्रिगर घटनाएं जैसे "सत्र कनेक्ट" या "डेटा उपलब्ध"।

समस्या: असिंक कॉलबैक और उसमें उठाई गई घटनाओं के कारण ईवेंट हैंडलर कार्यकर्ता थ्रेड संदर्भ में निष्पादित किया गया है। मैं एक इंटरमीडिएट परत का उपयोग करना चाहता हूं जो को ईवेंट हैंडलर को थ्रेड के संदर्भ में निष्पादित करने के लिए मजबूर करता है जिसने घटक को पहली जगह बनाया था।

उदाहरण कोड (हैंडलिंग आदि अपवाद से छीन लिया ...)

/// <summary> 
    /// Occurs when the connection is ended 
    /// </summary> 
    /// <param name="ar">The IAsyncResult to read the information from</param> 
    private void EndConnect(IAsyncResult ar) 
    { 
     // pass connection status with event 
     this.Socket.EndConnect(ar); 

     this.Stream = new NetworkStream(this.Socket); 

     // -- FIRE CONNECTED EVENT HERE -- 

     // Setup Receive Callback 
     this.Receive(); 
    } 


    /// <summary> 
    /// Occurs when data receive is done; when 0 bytes were received we can assume the connection was closed so we should disconnect 
    /// </summary> 
    /// <param name="ar">The IAsyncResult that was used by BeginRead</param> 
    private void EndReceive(IAsyncResult ar) 
    { 
     int nBytes; 
     nBytes = this.Stream.EndRead(ar); 
     if (nBytes > 0) 
     { 
      // -- FIRE RECEIVED DATA EVENT HERE -- 

      // Setup next Receive Callback 
      if (this.Connected) 
       this.Receive(); 
     } 
     else 
     { 
      this.Disconnect(); 
     } 
    } 
Async की प्रकृति के कारण

कुर्सियां ​​मेरी घटक का उपयोग सभी अनुप्रयोगों से अटे पड़े हैं "अगर (this.InvokeRequired) {... "और मैं चाहता हूं कि उपयोगकर्ता ड्रॉप-इन के प्रकार के रूप में मेरे घटक चिंता-मुक्त का उपयोग करने में सक्षम हो।

तो मैं उपयोगकर्ता को InvokeRequired (या, अलग-अलग रखता हूं, मैं उसी धागे में उठाए गए घटनाओं को कैसे लागू करता हूं, जिसने घटना को पहली जगह शुरू की थी) की जांच किए बिना घटनाओं को बढ़ाने के बारे में कैसे जाना होगा?

मैंने AsyncOperation, BackgroundWorkers, सिंक्रनाइज़िंग ऑब्जेक्ट्स, AsyncCallbacks और अन्य सामानों के टन के बारे में सामान पढ़ा है, लेकिन यह सब मेरे सिर स्पिन बनाता है।

मैं यह, निश्चित रूप से अनाड़ी, "समाधान" के साथ आया था, लेकिन यह कुछ स्थितियों में विफल (जब मेरे घटक उदाहरण के लिए एक स्थिर वर्ग के माध्यम से एक WinForms परियोजना से कहा जाता है)

/// <summary> 
    /// Raises an event, ensuring BeginInvoke is called for controls that require invoke 
    /// </summary> 
    /// <param name="eventDelegate"></param> 
    /// <param name="args"></param> 
    /// <remarks>http://www.eggheadcafe.com/articles/20060727.asp</remarks> 
    protected void RaiseEvent(Delegate eventDelegate, object[] args) 
    { 
     if (eventDelegate != null) 
     { 
      try 
      { 
       Control ed = eventDelegate.Target as Control; 
       if ((ed != null) && (ed.InvokeRequired)) 
        ed.Invoke(eventDelegate, args); 
       else 
        eventDelegate.DynamicInvoke(args); 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex.GetType()); 
       Console.WriteLine(ex.Message); 
       //Swallow 
      } 
     } 
    } 

किसी भी लगता है मदद की सराहना की जाएगी। अग्रिम में धन्यवाद!

संपादित करें: this thread के अनुसार मेरे सबसे अच्छे शर्त SyncrhonizationContext.Post उपयोग करने के लिए होगा, लेकिन मैं अपने स्थिति के लिए इसे लागू करने के लिए नहीं देख सकता।

उत्तर

2

ठीक है; इसलिए यहाँ क्या मैं कुछ और अधिक पढ़ने के बाद के साथ समाप्त हो गया है:

public class MyComponent { 
    private AsyncOperation _asyncOperation; 

    /// Constructor of my component: 
    MyComponent() { 
     _asyncOperation = AsyncOperationManager.CreateOperation(null); 
    } 

    /// <summary> 
    /// Raises an event, ensuring the correct context 
    /// </summary> 
    /// <param name="eventDelegate"></param> 
    /// <param name="args"></param> 
    protected void RaiseEvent(Delegate eventDelegate, object[] args) 
    { 
     if (eventDelegate != null) 
     { 
      _asyncOperation.Post(new System.Threading.SendOrPostCallback(
       delegate(object argobj) 
       { 
        eventDelegate.DynamicInvoke(argobj as object[]); 
       }), args); 
     } 
    } 
} 

अन्य समाधान यहां पोस्ट प्रकार का काम में प्रगति था। यहां पोस्ट किया गया समाधान लगता है (एमएसडीएन के मुताबिक) अब तक का सबसे अच्छा है। सुझाव बहुत स्वागत है।

0

शायद मैं इस मुद्दे को समझ नहीं रहा हूं, लेकिन ऐसा लगता है कि आप अपने असिनक राज्य में एक कस्टम ऑब्जेक्ट का संदर्भ दे सकते हैं।

मैं वर्णन करने के लिए निम्नलिखित उदाहरण एक साथ रखा;

प्रथम हम एक कॉलबैक वस्तु की है। इसमें 2 गुण हैं - एक नियंत्रण जिस पर कार्य प्रेषण और कॉल करने के लिए एक क्रिया;

public class Callback 
{ 
    public Control Control { get; set; } 
    public Action Method { get; set; } 
} 

तो मैं एक WinForms परियोजना कि जब कोड निष्पादित नहीं हो कुछ यादृच्छिक कोड एक और धागा (BeginInvoke का उपयोग) पर एक messagebox कॉल और फिर से पता चलता है।

private void Form1_Load(object sender, EventArgs e) 
    { 
     Action<bool> act = (bool myBool) => 
      { 
       Thread.Sleep(5000); 
      }; 

     act.BeginInvoke(true, new AsyncCallback((IAsyncResult result) => 
     { 
      Callback c = result.AsyncState as Callback; 
      c.Control.Invoke(c.Method); 

     }), new Callback() 
     { 
      Control = this, 
      Method =() => { ShowMessageBox(); } 
     });    
    } 

ShowMessageBox विधि यूआई थ्रेड पर चलते हैं और तरह लग रहा है चाहिए:

private void ShowMessageBox() 
    { 
     MessageBox.Show("Testing"); 
    } 

यह आपके लिए क्या देख रहे थे है?तब

public delegate void CallbackInvoker(Delegate method, params object[] args); 

public YourComponent(CallbackInvoker invoker) 
{ 
    m_invoker = invoker; 
} 

protected void RaiseEvent(Delegate eventDelegate, object[] args) 
{ 
    if (eventDelegate != null) 
    { 
     try 
     { 
      if (m_invoker != null) 
       m_invoker(eventDelegate, args); 
      else 
       eventDelegate.DynamicInvoke(args); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex.GetType()); 
      Console.WriteLine(ex.Message); 
      //Swallow 
     } 
    } 
} 

जब आप एक फार्म या अन्य नियंत्रण आप यह कर सकते हैं से अपने घटक का दृष्टांत:

+0

वास्तव में नहीं; मेरे घटक का उपयोग करते समय लोगों के लिए जटिल होने का यह तरीका है। सभी कार्य घटक का संदर्भ लेते हैं और कोड का उपयोग करते हैं जैसे: // आईपी, पोर्ट इत्यादि के साथ MyComponent प्रारंभ करें और फिर: MyComponent.SendString ("यह अच्छा है"); MyComponent घटनाओं को बढ़ाता है; चाहे वे जीयूआई या गैर-जीयूआई परियोजना द्वारा संभाले जाएं, इससे कोई फर्क नहीं पड़ता और मैं नहीं चाहता कि उपयोगकर्ता InvokeRequired की जांच के लिए ज़िम्मेदार हो। – ComponentBuilder

0

अपने घटक हमेशा एक ही धागे से इस्तेमाल किया जाना चाहिए, तो आप कुछ इस तरह कर सकता है

YourComponent c = new YourComponent(this.Invoke); 

एक गैर यूआई कार्यकर्ता धागे पर घटना को कतारबद्ध करने के लिए, इसमें कुछ प्रकार के कार्य क्यूइंग तंत्र होना चाहिए, फिर आप कार्यकर्ता धागे पर प्रतिनिधि को कतार में कॉलबैक इनवॉकर के हस्ताक्षर के साथ एक विधि दे सकते हैं।

+0

यह मेरे घटक का उपयोग कर उपयोगकर्ता के गोद में बहुत जटिलता डालता है। – ComponentBuilder

+0

वह जटिलता कहां है? घटक के कन्स्ट्रक्टर में एक प्रतिनिधि निर्दिष्ट करना? –

+0

मैं चाहता हूं कि मेरा घटक पारदर्शी रूप से असीमित हो (इसलिए एंडुसर नोटिस नहीं करता)। जब एंडुसर को किसी प्रतिनिधि (स्पष्ट) कारण के लिए एक प्रतिनिधि को पास करना होता है तो मुझे लगता है कि यह 'उपयोगकर्ता मित्रतापूर्ण' नहीं है। मुझे लगता है कि मेरा समाधान मिला है; लेकिन मुझे यकीन नहीं है कि यह "सबसे अच्छा" समाधान है या नहीं। – ComponentBuilder

1

मैं लगते मेरी समाधान पाया है करने के लिए:

private SynchronizationContext _currentcontext 

    /// Constructor of my component: 
    MyComponent() { 
     _currentcontext = WindowsFormsSynchronizationContext.Current; 
     //...or...? 
     _currentcontext = SynchronizationContext.Current; 
    } 

    /// <summary> 
    /// Raises an event, ensuring the correct context 
    /// </summary> 
    /// <param name="eventDelegate"></param> 
    /// <param name="args"></param> 
    protected void RaiseEvent(Delegate eventDelegate, object[] args) 
    { 
     if (eventDelegate != null) 
     { 
      if (_currentcontext != null) 
       _currentcontext.Post(new System.Threading.SendOrPostCallback(
        delegate(object a) 
        { 
         eventDelegate.DynamicInvoke(a as object[]); 
        }), args); 
      else 
       eventDelegate.DynamicInvoke(args); 
     } 
    } 

मैं अभी भी इस परीक्षण कर रहा हूँ, लेकिन यह ठीक से काम करने लगता है।

+0

जब आपका घटक UI थ्रेड पर नहीं बनाया गया है तो इस दृष्टिकोण के साथ क्या होगा? –

+0

मैं अपना नवीनतम संपादन पेस्ट करना भूल गया, जो जांचता है कि _currentcontext शून्य है या नहीं; यह अब तय है (और संपादित)। – ComponentBuilder

+0

मैंने अभी जांच की है, यदि आप घटक के कन्स्ट्रक्टर में सिंक्रनाइज़ेशन कॉन्टेक्स्ट को कैप्चर करते हैं और घटक गैर यूआई थ्रेड में बनाया गया है, तो आपका इवेंट डिलीगेट थ्रेडपूल पर निष्पादित किया जाएगा। –

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