2012-01-30 15 views
5

मैं एक बूस्ट :: सिग्नल 2 सिग्नल के बहुप्रचारित आमंत्रणों को क्रमबद्ध करना चाहता हूं ताकि यह सुनिश्चित किया जा सके कि ऑब्जेक्ट से राज्य परिवर्तनों के बारे में अधिसूचनाएं एक अच्छी तरह परिभाषित क्रम में स्लॉट पर पहुंचें।क्या कोई बढ़ावा :: सिग्नल 2 सिग्नल के आमंत्रण क्रमबद्ध करने का कोई मौजूदा तरीका है?

पृष्ठभूमि

मैं एक बहु कार्यक्रम में एक आंतरिक स्थिति के साथ एक वस्तु है। आंतरिक स्थिति के कुछ हिस्सों कार्यक्रम के अन्य भागों के लिए दिलचस्प हैं, और वस्तु को बढ़ावा :: signals2 संकेत, इस के समान का उपयोग करके राज्य में परिवर्तन को उजागर करता है:

class ObjectWithState { 
public: 
    enum State { 
     STATE_A, 
     STATE_B, 
     STATE_C, 
    }; 

    void OnEvent() { 
     State newState; 
     { 
      boost::lock_guard<boost::mutex> lock(m_Mutex); 
      // Process event and change state 
      m_State = ...; 
      newState = m_State; 
     } 
     m_OnStateChanged(newState); 
    } 

    // method to allow external objects to connect to the signal etc 
private: 
    boost::signals2::signal<void (State) > m_OnStateChanged; 
    boost::mutex m_Mutex; 
    State m_State; 
}; 

समस्या

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

इस समस्या को संभालने का एक तरीका सिग्नल से राज्य को हटाना होगा और सिर्फ श्रोताओं को सूचित करना होगा कि राज्य बदल गया है। फिर वे ऑब्जेक्ट को अपने राज्य के लिए पूछ सकते थे और राज्य को यह पता चल जाएगा कि जब ऑब्जेक्ट को या बाद में राज्य निकाल दिया गया था। मेरे परिदृश्य में, श्रोताओं को सभी राज्य परिवर्तनों के बारे में सूचित करने की आवश्यकता है, इसलिए यह विधि यहां काम नहीं करेगी।

मेरा अगला दृष्टिकोण की तरह कुछ होगा निम्नलिखित:

class ObjectWithState { 
public: 
    enum State { 
     STATE_A, 
     STATE_B, 
     STATE_C, 
    }; 

    void OnEvent() { 
     State newState; 
     boost::unique_future<void> waitForPrevious; 
     boost::shared_ptr<boost::promise<void> > releaseNext; 
     { 
      boost::lock_guard<boost::mutex> lock(m_Mutex); 
      // Process event and change state 
      m_State = ...; 
      newState = m_State; 
      waitForPrevious = m_CurrentInvocation->get_future(); 
      m_CurrentInvocation.reset(new boost::promise<void>()); 
      releaseNext = m_CurrentInvocation; 
     } 
     // Wait for all previous invocations of the signal to finish 
     waitForPrevious.get(); 

     // Now it is our turn to invoke the signal 
     // TODO: use try-catch/scoped object to release next if an exception is thrown 
     OnStateChanged(newState); 

     // Allow the next state change to use the signal 
     releaseNext->set_value(); 
    } 

    // method to allow external objects to connect to the signal etc 
private: 
    boost::signals2::signal<void (State) > m_OnStateChanged; 
    boost::mutex m_Mutex; 
    State m_State; 
    // Initialized with a "fulfilled" promise in the constructor 
    // or do special handling of initially empty promise above 
    boost::shared_ptr<boost::promise<void> > m_CurrentInvocation; 
}; 

मैं ऊपर कोड की कोशिश की है नहीं, तो यह कीड़े और संकलन त्रुटियों से अटे पड़े हो सकता है, लेकिन यह क्या निकालना संभव हो जाना चाहिए मैं बाद में हूँ मेरी आंत महसूस मुझे बताती है कि मैं इस प्रकार की समस्या का सामना करने वाला पहला व्यक्ति नहीं हूं और मैं अपने आप को कोशिश की गई और परीक्षण कोड का उपयोग करना पसंद करता हूं ... :) तो मेरा प्रश्न वास्तव में है:

क्या कोई पूर्ववर्ती तरीका है एक बढ़ावा के serialized invocations प्राप्त करने के लिए :: सिग्नल 2 सिग्नल (जैसे सिग्नल 2 पुस्तकालय या एक आम पैटर्न में बनाया गया)?

+0

"हालांकि, म्यूटेक्स को सिग्नल पर कॉल में नहीं रखा जा सकता है, क्योंकि इससे डेडलॉक्स हो सकते हैं।" क्या आप इस पर स्पष्टीकरण दे सकते हैं? क्या कुछ सिग्नल हैंडलर 'ऑब्जेक्ट विथस्टेट' की स्थिति बदल सकते हैं? क्या वे नए 'ऑनवेन्ट()' कॉल जेनरेट करेंगे? आप कैसे सुनिश्चित करते हैं कि आप अनंत रिकर्सन में प्रवेश नहीं करते हैं? – user1202136

+0

@ user1202136: हां। एक रिकर्सिव कॉल डेडलॉक पाने का एक तरीका होगा। एक और बेवकूफ तरीका यह है कि यदि आपके पास दो ऑब्जेक्ट विथस्टेट हैं और राज्य परिवर्तन के लिए हैंडलर सेट अप करते हैं, तो कुछ मामलों में दूसरे पर एक राज्य परिवर्तन ट्रिगर होता है। इससे पहले म्यूटेक्स को पकड़ने और दूसरे को लॉक करने की कोशिश करने वाले थ्रेड की ओर जाता है, और दूसरा धागा अन्य म्यूटेक्स को पकड़ता है और पहले लॉक करने की कोशिश करता है। अज्ञात कोड को कॉल करते समय एक सामान्य दिशानिर्देश लॉक को कभी नहीं पकड़ना है, चर्चा के लिए http://drdobbs.com/article/print?articleId=202802983&siteSectionName= देखें। – villintehaspam

उत्तर

0

मैं निम्नलिखित समाधान का प्रस्ताव करता हूं। लंबित सिग्नल की कतार बनाएं और एक अलग थ्रेड उन्हें प्रेषित करें। कोड लगभग इस प्रकार दिखता है:

class ObjectWithState { 
private: 
    bool running; 
    std::queue<State> pendingSignals; 
    boost::condition_variable cond; 
    boost::mutex mut; 

    void dispatcherThread() 
    { 
     while (running) 
     { 
      /* local copy, so we don't need to hold a lock */ 
      std::vector<State> pendingSignalsCopy; 

      /* wait for new signals, then copy them locally */ 
      { 
       boost::unique_lock<boost::mutex> lock(mut); 
       cond.wait(mut); 
       pendingSignalsCopy = pendingSignals; 
       pendingSignals.clear(); 
      } 

      /* dispatch */ 
      while (!pendingSignalsCopy.empty()) 
      { 
       State newState = pendingSignalsCopy.front(); 
       OnStateChanged(newState); 
       pendingSignalsCopy.pop(); 
      } 
     } 
    } 

public: 
    void OnEvent() 
    { 
     State newState; 
     ... 

     /* add signal to queue of pending signals and wake up dispatcher thread */ 
     { 
      boost::unique_lock<boost::mutex> lock(mut); 
      pendingSignals.push(state); 
      cond.notify_all(); 
     } 
    } 
}; 
+0

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

+0

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

+0

OnEvent के साथ OnStateChanged को प्रतिस्थापित करें, भ्रम के बारे में खेद है। अवरुद्ध करने में अंतर ठीक है जो मैं इंगित कर रहा हूं, कुछ मामलों में अधिसूचना सुनिश्चित करने के लिए धागे को अवरुद्ध करना आवश्यक है, अन्य में यह धागे को आगे बढ़ने में सक्षम होने की सुविधा होगी। – villintehaspam

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