2010-01-12 7 views
11

मुझे पता चला है कि boost :: सिग्नल 2 कनेक्टेड स्लॉट्स के आलसी विलोपन के प्रकार का उपयोग करता है, जिससे कनेक्शन का उपयोग करना मुश्किल होता है जो वस्तुओं के जीवनकाल का प्रबंधन करता है। मैं डिस्कनेक्ट होने पर सीधे स्लॉट को हटाने के लिए मजबूर करने का एक तरीका ढूंढ रहा हूं। मेरे कोड को अलग-अलग डिजाइन करके समस्या के आसपास काम करने के बारे में कोई भी विचार भी सराहना की जाती है!बूस्ट में स्लॉट को हटाने का बल :: सिग्नल 2

यह मेरा परिदृश्य है:

class ActualWorker { 
public: 
    boost::signals2<void()> OnWorkComplete; 
}; 

class Command : boost::enable_shared_from_this<Command> { 
public: 
    ... 

    void Execute() { 
     m_WorkerConnection = m_MyWorker.OnWorkDone.connect(boost::bind(&Command::Handle_OnWorkComplete, shared_from_this()); 

     // launch asynchronous work here and return 
    } 

    boost::signals2<void()> OnComplete; 

private: 
    void Handle_OnWorkComplete() { 
     // get a shared_ptr to ourselves to make sure that we live through 
     // this function but don't keep ourselves alive if an exception occurs. 
     shared_ptr<Command> me = shared_from_this(); 

     // Disconnect from the signal, ideally deleting the slot object 
     m_WorkerConnection.disconnect(); 

     OnComplete(); 

     // the shared_ptr now goes out of scope, ideally deleting this 
    } 

    ActualWorker m_MyWorker; 
    boost::signals2::connection m_WorkerConnection; 
}; 

वर्ग इस तरह के बारे में शुरू हो जाती है:: मैं एक कमान वर्ग कुछ है कि समय एसिंक्रोनस रूप लेता है, कुछ इस तरह (सरलीकृत) देख कर के लिए जिम्मेदार है

... 
boost::shared_ptr<Command> cmd(new Command); 
cmd->OnComplete.connect(foo); 
cmd->Execute(); 
// now go do something else, forget all about the cmd variable etcetera. 

कमांड क्लास खुद को एक साझा_ptr प्राप्त करके खुद को जीवित रखती है जो बूस्ट :: बाइंड का उपयोग करके वास्तविक वर्कर सिग्नल से जुड़ी है।

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

मैं इस साझाकर्ता के बजाय इस पॉइंटर का उपयोग करके बाध्यकारी करके और उसके ऑब्जेक्ट को साझा_ptr का उपयोग करके जीवित रखकर काम कर सकता हूं जिसे मैं हैंडलर फ़ंक्शन में रिलीज़ करता हूं, लेकिन यह डिजाइन को थोड़ा अधिक जटिल बनाता है। डिस्कनेक्ट होने पर स्लॉट को हटाने के लिए सिग्नल 2 को मजबूर करने का कोई तरीका है? या डिजाइन को सरल बनाने के लिए मैं कुछ और कर सकता हूं?

कोई टिप्पणी की सराहना की जाती है!

उत्तर

1

मैंने सिग्नल के अपने स्वयं के (सबसेट) कार्यान्वयन को समाप्त कर दिया, मुख्य आवश्यकता यह है कि एक स्लॉट को कनेक्शन से डिस्क द्वारा नष्ट किया जाना चाहिए :: डिस्कनेक्ट()।

कार्यान्वयन स्लॉट कार्यान्वयन सूचक से एक मानचित्र में सभी स्लॉट्स को एक सूची/वेक्टर के बजाय स्लॉट कार्यान्वयन के लिए एक स्क्रिप्ट पर संग्रहीत सिग्नल की तर्ज पर जाता है, जिससे व्यक्तिगत स्लॉट तक त्वरित पहुंच प्रदान होती है स्लॉट्स। एक स्लॉट कार्यान्वयन मेरे मामले में मूल रूप से एक बढ़ावा :: समारोह है।

कनेक्शन के लिए आंतरिक कार्यान्वयन वर्ग में एक कमजोर_पीआरआर है और स्लॉट कार्यान्वयन प्रकार के लिए कमजोर_प्टर है ताकि सिग्नल को दायरे से बाहर निकलने की अनुमति मिल सके और सिग्नल मानचित्र में कुंजी के रूप में स्लॉट पॉइंटर का उपयोग किया जा सके। संकेत है कि कनेक्शन अभी भी सक्रिय है या नहीं (कच्चे सूचक का उपयोग नहीं कर सकता है क्योंकि संभावित रूप से पुन: उपयोग किया जा सकता है)।

डिस्कनेक्ट होने पर, इन दोनों कमजोर पॉइंटर्स को साझा_ptrs में परिवर्तित कर दिया जाता है और यदि इनमें से दोनों सफल होते हैं, तो सिग्नल कार्यान्वयन को पॉइंटर द्वारा दिए गए स्लॉट को डिस्कनेक्ट करने के लिए कहा जाता है। यह नक्शे से इसे सरल मिटाकर किया जाता है।

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

सिग्नल निकाल दिए जाने के लिए कोड को सरल बनाने के लिए, मैं इस दौरान सभी स्लॉट को डिस्कनेक्ट करने के लिए मजबूर कर रहा हूं। यह boost :: सिग्नल 2 से अलग है, जो सिग्नल को फायर करते समय डिस्कनेक्शन/कनेक्शन को संभालने के लिए उन्हें कॉल करने से पहले स्लॉट की सूची की प्रतिलिपि बनाता है।

उपर्युक्त मेरे परिदृश्य के लिए अच्छी तरह से काम करता है, जहां ब्याज का संकेत बहुत ही कम हो जाता है (और उस मामले में केवल एक बार) लेकिन बहुत सारे अल्पकालिक कनेक्शन हैं जो अन्यथा उपयोग करते समय भी बहुत मेमोरी का उपयोग करते हैं सवाल में उल्लिखित चाल।

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

1

व्यवहार स्कॉप्ड_कनेक्शन के साथ और अधिक सख्त है?

तो, बजाय:

void Execute() { 
    m_WorkerConnection = m_MyWorker.OnWorkDone.connect(boost::bind 
     (&Command::Handle_OnWorkComplete, shared_from_this()); 

    // launch asynchronous work here and return 
} 

... 

boost::signals2::connection m_WorkerConnection; 
इसके बजाय

का उपयोग कर:

void Execute() { 
    boost::signals2::scoped_connection m_WorkerConnection 
     (m_MyWorker.OnWorkDone.connect(boost::bind 
     (&Command::Handle_OnWorkComplete, shared_from_this())); 

    // launch asynchronous work here and return 
} // connection falls out of scope 

(एक boost::signals2::connection से कॉपी-निर्माण)

मैं संकेत किसी भी प्रकार का तो यह और भी है के लिए इस्तेमाल नहीं किया है किसी और चीज की तुलना में अनुमान के मुकाबले, लेकिन Execute() के बाद आपको disconnect() की आवश्यकता नहीं होगी, क्योंकि scoped_connection आपके लिए इसे संभालता है। वास्तव में आपकी समस्या को हल करने के बजाय 'डिजाइन को सरल बनाएं'। लेकिन इसका मतलब यह हो सकता है कि आप Execute() और फिर तत्काल ~Command() (या delete साझा_ptr) कर सकते हैं।

उम्मीद है कि मदद करता है।

संपादित करें: और Execute() द्वारा तत्काल ~Command() मुझे स्पष्ट रूप से आपके कमांड ऑब्जेक्ट के बाहर से मतलब है। जब आप इसे निष्पादित करने के लिए कमांड बनाते हैं, तो आपको यह कहने में सक्षम होना चाहिए:

cmd->Execute(); 
delete cmd; 

या इसी तरह।

+0

निष्पादित समारोह के अंत में डिस्कनेक्ट कर रहा है के लिए मेरे पैच है सब पर जोड़ने का उद्देश्य धरा - यानी मैं एक कॉलबैक नहीं मिलेगा जब अतुल्यकालिक काम पूरा हो गया है तो आपका सुझाव वास्तव में व्यवहार्य नहीं है। – villintehaspam

3

boost::signals2 कनेक्ट/इनवॉक के दौरान स्लॉट को साफ़ करता है।

तो यदि सभी स्लॉट स्वयं को सिग्नल से डिस्कनेक्ट करते हैं, तो सिग्नल का आह्वान करते हुए दूसरी बार कुछ भी कॉल नहीं होगा लेकिन इसे स्लॉट साफ़ करना चाहिए।

अपनी टिप्पणी का उत्तर देने के लिए, हाँ, फिर से सिग्नल का आह्वान करना सुरक्षित नहीं है यदि अन्य स्लॉट जुड़े हुए हैं, क्योंकि उन्हें फिर से बुलाया जाएगा। उस स्थिति में मेरा सुझाव है कि आप दूसरी तरफ जाएं और डमी स्लॉट को कनेक्ट करें, फिर जब आपका "असली" स्लॉट लागू होता है तो उसे डिस्कनेक्ट करें। एक और स्लॉट कनेक्ट करने से पुराने कनेक्शन साफ ​​हो जाएंगे, इसलिए आपका स्लॉट जारी किया जाना चाहिए।

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

+0

यह वही है जो मैंने इस प्रश्न में लिखा था, कि मैं इससे बचना चाहूंगा। मुझे खेद है कि प्रश्न पाठ इसे सही तरीके से व्यक्त नहीं करता है। वैसे भी, संकेत फिर से नहीं बुलाया जाता है - और मुझे लगता है कि आप ऑब्जेक्ट को हटाने के साधन के रूप में इसे दूसरी बार आमंत्रित करने का सुझाव नहीं दे रहे हैं? – villintehaspam

+0

यह वही है जो मैं सुझाव दे रहा था, अजीब शब्द के लिए खेद है। boost :: signals2 "कचरा इकट्ठा" एक आक्रमण के दौरान डिस्कनेक्ट स्लॉट एकत्र करता है, इसलिए यदि आप डिस्कनेक्ट होने के बाद इसे फिर से आमंत्रित करते हैं तो इसे अपनी ऑब्जेक्ट को हटाना चाहिए। –

+0

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

2

यह बढ़ावा :: संकेत 2 का एक अविश्वसनीय रूप से कष्टप्रद पहलू है।

जिस दृष्टिकोण को मैंने हल करने के लिए लिया वह सिग्नल को स्कॉप्ड_प्टर में संग्रहीत करना है, और जब मैं सभी स्लॉट्स के डिस्कनेक्शन को मजबूर करना चाहता हूं, तो मैं सिग्नल हटा देता हूं। यह केवल उन मामलों में काम करता है जब आप सभी कनेक्शन को सिग्नल में मजबूती से डिस्कनेक्ट करना चाहते हैं।

1

मैं एक ही समस्या पर ठोकर खाई और मुझे वास्तव में एपीआई में किसी तरह का स्पष्ट सफाई याद आती है।

मेरे परिदृश्य में मैं कुछ प्लग-इन डीएलएस को उतार रहा हूं और मुझे यह आश्वस्त करना है कि कोई लटकती वस्तुएं (स्लॉट्स) नहीं हैं जो अनलोड किए गए डीएल में कोड (vftables या जो भी) रहती हैं। आलसी हटाने की सामग्री के कारण बस स्लॉट डिस्कनेक्ट नहीं किया गया था।

template <typename Signature> 
struct MySignal 
{ 
    // ... 

    template <typename Slot> 
    void disconnect (Slot&& s) 
    { 
    mPrivate.disconnect (forward (s)); 
    // connect/disconnect dummy slot to force cleanup of s 
    mPrivate.connect (&MySignal::foo); 
    mPrivate.disconnect (&MySignal::foo); 
    } 

private: 
    // dummy slot function with matching signature 
    // ... foo (...) 

private: 
    ::boost::signals2::signal<Signature> mPrivate; 
}; 

दुर्भाग्य से यह क्योंकि connect() केवल कुछ सफाई करता है काम नहीं किया:

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

विकल्प के अभाव मैं मूल signal वर्ग पैच में समाप्त हो गया में (संपादित करें:। मैं वास्तव में निर्मित एक समाधान की सराहना करेंगे इस पैच मेरी अंतिम उपाय था)। मेरा पैच कोड की लगभग 10 पंक्तियां है और cleanup_connections() विधि signal पर एक सार्वजनिक जोड़ता है। मेरा सिग्नल रैपर डिस्कनेक्टिंग विधियों के अंत में सफाई को आमंत्रित करता है। इस दृष्टिकोण ने मेरी समस्याओं का समाधान किया और मुझे अब तक कोई प्रदर्शन समस्या नहीं आई है।

संपादित करें: यहाँ बढ़ावा 1.5.3

Index: signals2/detail/signal_template.hpp 
=================================================================== 
--- signals2/detail/signal_template.hpp 
+++ signals2/detail/signal_template.hpp 
@@ -220,6 +220,15 @@ 
      typedef mpl::bool_<(is_convertible<T, group_type>::value)> is_group; 
      do_disconnect(slot, is_group()); 
     } 
+  void cleanup_connections() const 
+  { 
+   unique_lock<mutex_type> list_lock(_mutex); 
+   if(_shared_state.unique() == false) 
+   { 
+   _shared_state.reset(new invocation_state(*_shared_state, _shared_state->connection_bodies())); 
+   } 
+   nolock_cleanup_connections_from(false, _shared_state->connection_bodies().begin()); 
+  } 
     // emit signal 
     result_type operator()(BOOST_SIGNALS2_SIGNATURE_FULL_ARGS(BOOST_SIGNALS2_NUM_ARGS)) 
     { 
@@ -690,6 +699,10 @@ 
     { 
     (*_pimpl).disconnect(slot); 
     } 
+  void cleanup_connections() 
+  { 
+  (*_pimpl).cleanup_connections(); 
+  } 
     result_type operator()(BOOST_SIGNALS2_SIGNATURE_FULL_ARGS(BOOST_SIGNALS2_NUM_ARGS)) 
     { 
     return (*_pimpl)(BOOST_SIGNALS2_SIGNATURE_ARG_NAMES(BOOST_SIGNALS2_NUM_ARGS)); 
संबंधित मुद्दे