2013-01-17 11 views
13

का उपयोग कर पर्यवेक्षक पैटर्न observer pattern से मैं एक सुरक्षित Subject कक्षा लिखने की कोशिश कर रहा हूं। मुझे पता है कि अगर weak_ptr का उपयोग कर सबसे अच्छा तरीका है इस तरह से IObserver उदाहरणों स्टोर करने के लिए है चाहता हूँ कि:weak_ptr

  • जाने के बाद उसे free'd कर दिया गया है एक IObserver उदाहरण उपयोग करने के लिए संभव नहीं है।
  • Subject वर्ग IObserver संदर्भ free'd किया जाना चाहिए (lapsed listener problem) पर पकड़ नहीं करता है।
  • Subject कक्षा धागा सुरक्षित होना चाहिए।

दुर्भाग्य से, हमारी कोडिंग मानकों का कहना है कि हम को बढ़ावा देने का उपयोग करने की अनुमति नहीं है। मुझे लगता है कि मैं पिछले जीवन में एक बुरे व्यक्ति था। सौभाग्य से, मुझे सी ++ 11 का उपयोग करने की अनुमति है (विजुअल स्टूडियो 2012 के साथ क्या भेजा जाता है)।

यहां नमूना Observer वर्ग है।

// Observer interface that supports notify() method 
class IObserver 
{ 
public: 
    virtual void notify() const = 0; 
    virtual ~IObserver() {} 
}; 

// Concrete observer implementation that prints a message 
class Observer : public IObserver 
{ 
public: 
    Observer(const std::string& message) : m_message(message){} 

    void notify() const { 
     printf("%s\r\n", m_message.c_str()); 
    } 

private: 
    std::string m_message; 
}; 

और यहाँ Subject वर्ग है।

int main(int argc, wchar_t* argv[]) 
{ 

    Subject subject; 
    auto observerHello = std::make_shared<Observer>("Hello world"); 
    subject.registerObserver(observerHello); 
    { 
     // Create a scope to show unregistration. 
     auto observerBye = std::make_shared<Observer>("Good bye"); 
     subject.registerObserver(observerBye); 

     subject.doNotify(); 
    } 
    printf("%s\r\n", "Observer good bye is now be destructed"); 
    subject.doNotify(); 
    return 0; 
} 

weak_ptr धागा सुरक्षित की मेरी उपयोग है:

// Subject which registers observers and notifies them as needed. 
class Subject 
{ 
public: 
    // Use shared_ptr to guarantee the observer is valid right now 
    void registerObserver(const std::shared_ptr<IObserver>& o) 
    { 
     std::lock_guard<std::mutex> guard(m_observersMutex); 
     m_observers.push_back(o); 
    } 

    void unregisterObserver(const std::shared_ptr<IObserver>& o) 
    { 
     std::lock_guard<std::mutex> guard(m_observersMutex); 
     // Code to remove the observer from m_observersMutex 
    } 

    // This is a method that is run in its own thread that notifies observers of some event 
    void doNotify() 
    { 
     std::lock_guard<std::mutex> guard(m_observersMutex); 
     // Notify any valid observers of events. 
     std::for_each(m_observers.cbegin(), m_observers.cend(), 
      [](const std::weak_ptr<IObserver>& o) 
     { 
      auto observer = o.lock(); 
      if (observer) { 
       observer->notify(); 
      } 
     }); 

     // Remove any dead observers. These are ones which have expired(). 
     m_observers.erase(std::remove_if(m_observers.begin(), m_observers.end(), 
      [](const std::weak_ptr<IObserver>& o) 
     { 
      return o.expired(); 
     }), m_observers.end()); 

    } 


private: 
    std::vector<std::weak_ptr<IObserver>> m_observers; 
    std::mutex m_observersMutex; 
}; 

यहाँ कुछ कोड है कि Subject अभ्यास है? यहां से https://stackoverflow.com/a/2160422/1517648 I लगता है यह है।

क्या यह लापता श्रोता समस्या को हल करने का एक वैध तरीका है?

+0

नहीं 2012 रेंज छोरों के लिए आधारित है करता है? आप for_each का उपयोग क्यों कर रहे हैं? (प्रश्न के लिए अप्रासंगिक, हे) – David

+0

यह सिर्फ एक आदत है, इसके अलावा इसका कोई कारण नहीं है जिसका उपयोग मैं करता हूं। – Steve

उत्तर

11

मैं आपके doNotify का थोड़ा सा हिस्सा लेता हूं - मान लीजिए कि आपके द्वारा आग लगने वाले पर्यवेक्षक में कुछ ऐसा हो रहा है जो पर्यवेक्षकों को जोड़ रहा है या हटा रहा है? - बुरी चीजें होती हैं (दुर्घटनाओं सहित)। या किसी अन्य धागे की कार्रवाई पर अवरुद्ध, जो पर्यवेक्षक जोड़ने की कोशिश कर रहा है? - बुरी चीजें होती हैं (deadlocks!)

यह हल करने के लिए मुश्किल है। असल में, यह पुनर्वितरण के साथ एक समस्या है।

कभी भी लॉक होने पर कभी भी अपने कोड पर नियंत्रण न छोड़ें। कॉलबैक कॉल करते समय लॉक पकड़ना नो-नो है।

तो, कम से कम:

ताला तो कॉपी अपनी सूची तो अनलॉक। इस प्रतिलिपि करते समय, आप कालबाह्य पर्यवेक्षकों को भी हटा सकते हैं (मूल, और प्रतिलिपि, सूची दोनों से)।

फिर प्रतिलिपि सूची से पर्यवेक्षकों को आग लगाना।

इससे कुछ समस्याएं अनसुलझे होती हैं। इस तथ्य के रूप में कि एक पर्यवेक्षक को हटाने की गारंटी नहीं है कि इसे भविष्य में नहीं बुलाया जाएगा! इसका मतलब यह है कि आखिरकार इसे नहीं बुलाया जाएगा।

यह कितना महत्वपूर्ण है कि आप कैसे सुनते हैं इस पर निर्भर करता है।

एक दृष्टिकोण जो काम कर सकता है वह एक कार्य कतार है जिसमें कतार में एक कार्य को मारने/निकालने/सूचित करने में शामिल है (कतार में एक कार्य को मारना बहुत कम परेशान करता है)। अब सभी सिंक्रनाइज़ेशन कतार पर है।यदि आप एक गैर-अवरुद्ध लॉक-फ्री कतार लिखने के लिए तैयार नहीं हैं, तो अधिसूचना कोड केवल std::move कतार को अनलॉक कर सकता है, फिर इसे निष्पादित करने के लिए आगे बढ़ता है। या आप एक कतार लिख सकते हैं जैसे pop ब्लॉक पढ़ने के लिए कुछ है, और push ब्लॉक नहीं करता है।

एक त्वरित और गंदे "कॉपी और प्रसारण" इस प्रकार दिखाई देंगे:

std::vector<std::shared_ptr<IObserver>> targets; 
{ 
    std::lock_guard<std::mutex> guard(m_observersMutex); 
    m_observers.erase(std::remove_if(m_observers.begin(), m_observers.end(), 
     [&targets](const std::weak_ptr<IObserver>& o) 
    { 
     std::shared_ptr<IObserver> ptr = o.lock(); 
     if (ptr) { 
     targets.push_back(ptr); 
     return false; 
     } else { 
     return true; 
     } 
    }), m_observers.end()); 
} 

for(auto& target:targets) { 
    target->notify(); 
} 
+0

यह वास्तव में सहायक है, धन्यवाद :) अभी के लिए मैं प्रतिलिपि और प्रसारण के साथ जाऊंगा क्योंकि एक पर्यवेक्षक को हटाने के बाद एक घटना प्राप्त करना कोई बड़ा सौदा नहीं है। यद्यपि गैर-अवरुद्ध लॉक-मुक्त कतारों पर पढ़ना होगा। – Steve

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