2012-03-16 14 views
5

उदाहरण के लिए की जीवन भर के लिए स्मृति में रखा हो, इस पर विचार करें:Do 'मध्यवर्ती IObservables' अंतिम ग्राहकों के बिना जड़ IObservable

public IDisposable Subscribe<T>(IObserver<T> observer) 
    { 
     return eventStream.Where(e => e is T).Cast<T>().Subscribe(observer); 
    } 

eventStream घटनाओं की एक लंबी रहते थे स्रोत है। एक अल्पकालिक ग्राहक इस विधि का उपयोग कुछ समय के लिए सदस्यता लेने के लिए करेगा, और फिर पर Dispose पर कॉल करके सदस्यता समाप्त कर देगा। एक Where() विधि है कि शायद eventStream द्वारा स्मृति में आयोजित किया जाता है से लौट आए, और एक वापस आ -

हालांकि, जबकि eventStream अभी भी मौजूद है और स्मृति में रखा जाना चाहिए, वहाँ 2 नए IObservables इस विधि के द्वारा बनाई गई कर दिया गया है Cast<T>() विधि द्वारा संभवतः Where() विधि द्वारा लौटाए गए द्वारा स्मृति में आयोजित किया गया है।

इन 'इंटरमीडिएट IObservables' (उनके लिए एक बेहतर नाम क्या होगा?) साफ हो जाएंगे? या अब वे eventStream के जीवनकाल के लिए मौजूद होंगे, भले ही उनके पास सब्सक्रिप्शन न हों और उनके स्रोत IObservable को छोड़कर कोई और संदर्भ न दे और फिर कभी सदस्यता नहीं मिलेगी?

यदि वे अपने माता-पिता को सूचित करके साफ कर रहे हैं तो उनके पास सब्सक्रिप्शन नहीं है, वे कैसे जानते हैं कि किसी और ने उन्हें संदर्भ नहीं दिया है और कुछ समय बाद उनको सब्सक्राइब किया जा सकता है?

+0

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

+0

मैंने सोचा कि शुरुआत में भी, लेकिन मेरे प्रश्न की अंतिम वाक्य पढ़ें।अस्थायी पर्यवेक्षकों को पता है कि किसी और ने उनके लिए कोई संदर्भ नहीं लिया है और किसी बिंदु पर बाद में सदस्यता ली जा सकती है? – Tyson

+0

मुझे नहीं पता कि वास्तव में क्या होता है, क्षमा करें। लेकिन एक तरीका यह _could_ काम है। सदस्यता वृद्धि संख्याओं (जो अभिभावक श्रृंखला को बुलबुला करता है) सदस्यता लें। और उन्हें कम करें, अगर यह शून्य हो जाता है तो इसका निपटान करें। –

उत्तर

2

हालांकि, जबकि eventStream अभी भी मौजूद है और स्मृति में रखा जाना चाहिए, वहाँ 2 नए इस विधि के द्वारा बनाई गई IObservables रहा है - एक विधि कहाँ() द्वारा दिया कि शायद eventStream द्वारा स्मृति में आयोजित किया जाता है, और एक कास्ट() विधि द्वारा लौटाया गया है जो संभवतः कहां() विधि द्वारा लौटाए गए द्वारा स्मृति में आयोजित किया जाता है।

आपके पास यह पिछड़ा है। चलो चल रहा है की श्रृंखला के माध्यम से चलो चलते हैं।

IObservable<T> eventStream; //you have this defined and assigned somewhere 

public IDisposable Subscribe<T>(IObserver<T> observer) 
{ 
    //let's break this method into multiple lines 

    IObservable<T> whereObs = eventStream.Where(e => e is T); 
    //whereObs now has a reference to eventStream (and thus will keep it alive), 
    //but eventStream knows nothing of whereObs (thus whereObs will not be kept alive by eventStream) 
    IObservable<T> castObs = whereObs.Cast<T>(); 
    //as with whereObs, castObs has a reference to whereObs, 
    //but no one has a reference to castObs 
    IDisposable ret = castObs.Subscribe(observer); 
    //here is where it gets tricky. 
    return ret; 
} 

क्या ret करता है या एक संदर्भ नहीं है विभिन्न observables के क्रियान्वयन पर निर्भर। मैंने आरएक्स लाइब्रेरी में रिफ्लेक्टर में जो देखा है और ऑपरेटर मैंने खुद को लिखा है, ज्यादातर ऑपरेटर ऐसे डिस्पोजेबल वापस नहीं लौटाते हैं जिनके पास ऑपरेटर का संदर्भ स्वयं ही दिखता है।

उदाहरण के लिए, Where की एक बुनियादी कार्यान्वयन की तरह कुछ (संपादक में सीधे टाइप किया, कोई त्रुटि हैंडलिंग)

IObservable<T> Where<T>(this IObservable<T> source, Func<T, bool> filter) 
{ 
    return Observable.Create<T>(obs => 
     { 
     return source.Subscribe(v => if (filter(v)) obs.OnNext(v), 
           obs.OnError, obs.OnCompleted); 
     } 
} 

सूचना है कि डिस्पोजेबल लौटे पर्यवेक्षक के माध्यम से फिल्टर समारोह के लिए एक संदर्भ होगा होगा यह बनाया गया है, लेकिन Where देखने योग्य नहीं होगा। Cast को उसी पैटर्न का उपयोग करके आसानी से कार्यान्वित किया जा सकता है। संक्षेप में, ऑपरेटर पर्यवेक्षक रैपर कारखानों बन जाते हैं।

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

सुपरकैट की टिप्पणी के लिए संपादित करें, चलिए देखते हैं कि कंपाइलर इसे फिर से लिख सकता है या आप इसे बंद किए बिना कैसे कार्यान्वित करेंगे।

class WhereObserver<T> : IObserver<T> 
{ 
    WhereObserver<T>(IObserver<T> base, Func<T, bool> filter) 
    { 
     _base = base; 
     _filter = filter; 
    } 

    IObserver<T> _base; 
    Func<T, bool> _filter; 

    void OnNext(T value) 
    { 
     if (filter(value)) _base.OnNext(value); 
    } 

    void OnError(Exception ex) { _base.OnError(ex); } 
    void OnCompleted() { _base.OnCompleted(); } 
} 

class WhereObservable<T> : IObservable<T> 
{ 
    WhereObservable<T>(IObservable<T> source, Func<T, bool> filter) 
    { 
     _source = source; 
     _filter = filter; 
    } 

    IObservable<T> source; 
    Func<T, bool> filter; 

    IDisposable Subscribe(IObserver<T> observer) 
    { 
     return source.Subscribe(new WhereObserver<T>(observer, filter)); 
    } 
} 

static IObservable<T> Where(this IObservable<T> source, Func<T, bool> filter) 
{ 
    return new WhereObservable(source, filter); 
} 

आप देख सकते हैं कि पर्यवेक्षक नमूदार है कि यह उत्पन्न और नमूदार पर्यवेक्षकों यह बनाता है ट्रैक करने के लिए कोई ज़रूरत नहीं है के लिए किसी भी संदर्भ की जरूरत नहीं है। हमने अपनी सदस्यता से वापस आने के लिए कोई नया आईडीस्पोजेबल भी नहीं बनाया है।

असल में, आरएक्स में अज्ञात पर्यवेक्षक/पर्यवेक्षक के लिए कुछ वास्तविक कक्षाएं हैं जो प्रतिनिधियों को लेती हैं और उन प्रतिनिधियों को इंटरफ़ेस कॉल अग्रेषित करती हैं। यह उन प्रतिनिधियों को बनाने के लिए बंद का उपयोग करता है। कंपाइलर को कक्षाओं को उत्सर्जित करने की आवश्यकता नहीं होती है जो वास्तव में इंटरफेस को लागू करते हैं, लेकिन अनुवाद की भावना एक ही है।

+0

धन्यवाद ग्रिड, इस तरह से कदम उठा रहा है और नमूना 'कहां' कार्यान्वयन ने इसे मेरे लिए क्लिक करने में मदद की। (मुझे वास्तव में अपने गधे को बंद करना चाहिए और प्रतिबिंबित करना चाहिए)। मैंने अपना जवाब पोस्ट कर दिया है क्योंकि यह एक टिप्पणी के रूप में फिट नहीं होगा और मैं आपके तर्क के परिणामों के साथ संयुक्त उत्तर को सारांशित करना चाहता था। क्या आप इसके माध्यम से पढ़ सकते हैं और पुष्टि कर सकते हैं? मैं इस सवाल को अभी के लिए खोल दूंगा क्योंकि मुझे यकीन नहीं है कि मुझे आपका जवाब या मेरा स्वीकार करना चाहिए या नहीं? आपकी मदद की, लेकिन मेरा अब सीधे मेरे प्रश्न में उलझन में आया था। – Tyson

+0

जब आप कहते हैं कि डिस्पोजेबल में 'कहां' देखने योग्य नहीं है, तो क्या आप बंद करके आयोजित खाते संदर्भों को ध्यान में रखते हैं? मैं बंद होने की नींद लेता हूं क्योंकि वे संदर्भों को अन्यथा स्पष्ट होने की तुलना में बहुत लंबे समय तक रखा जा सकता है। – supercat

+0

@supercat हाँ मैं था। मेरा संपादन देखें। यह * पर्यवेक्षक * है कि निपटान (मूल स्रोत द्वारा बनाया गया) संदर्भित करेगा। –

0

IObservable लागू करने वाला एक वर्ग केवल एक नियमित वस्तु है। जब जीसी चलता है और इसे कोई संदर्भ नहीं दिखता है तो यह साफ हो जाएगा। यह new object() कब साफ हो जाता है "के अलावा कुछ भी नहीं है। स्मृति उपयोग के अलावा, चाहे वे साफ़ हो जाएं, आपके प्रोग्राम के लिए दृश्यमान नहीं होना चाहिए।

+0

हां, और बिल्कुल नहीं, नहीं। इन तरह के आरएक्स प्रश्नों में, प्रत्येक 'अपस्ट्रीम' देखने योग्य प्रत्येक 'डाउनस्ट्रीम' को देखने योग्य (संदर्भ में IObserver में पारित होने के माध्यम से) का संदर्भ रखता है। यह जड़ के जीवनकाल के लिए स्मृति में अवलोकनों की पूरी श्रृंखला को देखता है। इससे मुद्दों का कारण बनता है यदि इन इंटरमीडिएट अवलोकनों की बड़ी संख्या बनाई जाती है और प्रसंस्करण घटनाओं के आसपास लटकती है, भले ही उनके अंतिम 'एंड-पॉइंट' पर्यवेक्षकों (आपके वास्तविक ऐप तर्क चलाने वाले पर्यवेक्षकों) ने बहुत पहले सदस्यता समाप्त कर दी हो। – Tyson

+0

@ टायसन जो 'IObservable' लागू करने वाले वर्ग के लिए विशिष्ट लगता है। ऐसा कोई कारण नहीं है कि एक अवलोकन योग्य को किसी अन्य वस्तु का निरीक्षण करना जारी रखना चाहिए, यदि यह स्वयं ही अप्रबंधित है, और यदि यह केवल उस अन्य वस्तु को देखने से रोकता है, तो यह अव्यवस्थित हो जाएगा और ठीक से साफ हो जाएगा। – hvd

+0

@ एचवीडी: समस्या यह है कि जब एक्स को "निरीक्षण" करने के लिए कहा जाता है, तो इसका वास्तव में क्या अर्थ है कि एक्स बदलते समय एक्स को अधिसूचनाएं स्वीकार करनी होंगी। चाहे कोई भी जीवित है या नहीं, एक्स कुछ ऐसी चीजों के बारे में परवाह करता है, एक्स ऐसी सूचनाओं के जवाब में कर सकता है, तथ्य यह है कि एक्स इस तरह की अधिसूचनाओं को स्वीकार करता है, आमतौर पर इसका परिणाम तब तक जीवित रहता है जब तक वाई है। – supercat

0

यदि कोई ऑब्जेक्ट घटनाओं की सदस्यता लेता है, चाहे अपने स्वयं के उपयोग के लिए, या अन्य वस्तुओं को अग्रेषित करने के उद्देश्य से, उन घटनाओं के प्रकाशक आमतौर पर इसे जीवित रखेंगे, भले ही कोई और नहीं करेगा। अगर मैं आपकी स्थिति को सही ढंग से समझ रहा हूं, तो आपके पास ऐसी वस्तुएं हैं जो उन्हें शून्य या अधिक अन्य ग्राहकों को अग्रेषित करने के उद्देश्य से घटनाओं की सदस्यता लेती हैं। मैं सुझाव दूंगा कि यदि संभव हो तो आपको अपने इंटरमीडिएट IObservables को डिज़ाइन करना चाहिए ताकि वे अपने माता-पिता से किसी ईवेंट की सदस्यता ले सकें, जब तक कोई उनके द्वारा किसी ईवेंट की सदस्यता नहीं ले लेता है, और जब भी वे अपने अंतिम ग्राहक सदस्यता छोड़ते हैं तो वे अपने माता-पिता के ईवेंट से सदस्यता छोड़ देंगे। यह व्यावहारिक है या नहीं, माता-पिता और बच्चे IObservables के थ्रेडिंग संदर्भों पर निर्भर करेगा। आगे ध्यान दें कि (फिर से थ्रेडिंग संदर्भ के आधार पर) उस मामले से निपटने के लिए लॉकिंग की आवश्यकता हो सकती है जहां एक नया ग्राहक एक ही समय में (जो होता) अंतिम ग्राहक छोड़ देता है। भले ही अधिकांश ऑब्जेक्ट्स सब्सक्रिप्शन और सदस्यता रद्द करने के परिदृश्यों को लॉक करने के बजाए CompareExchange का उपयोग करके संभाला जा सके, यह अक्सर जुड़े हुए सदस्यता सूचियों वाले परिदृश्यों में अनजान है।

अपने वस्तु एक सूत्रण संदर्भ में जो माता पिता की सदस्यता और सदस्यता खत्म करने के तरीकों (IMHO साथ संगत नहीं है अपने बच्चों से सदस्यता और unsubscriptions प्राप्त होगा, तो IObservable आवश्यक होना चाहिए था कि सभी वैध कार्यान्वयन मनमाना सूत्रण संदर्भ से सदस्यता और सदस्यता खत्म करने की अनुमति देते हैं, लेकिन हां यह नहीं है) आपके पास सृजन पर तत्काल इंटरमीडिएट IObservable होने के अलावा कोई विकल्प नहीं है, अपनी ओर से सदस्यता को संभालने के लिए प्रॉक्सी ऑब्जेक्ट बनाएं, और उस ऑब्जेक्ट को अभिभावक के ईवेंट की सदस्यता लें। फिर अपनी खुद की वस्तु रखें (जिस पर प्रॉक्सी के पास केवल एक कमजोर संदर्भ होगा) एक फाइनलज़र शामिल है जो प्रॉक्सी को सूचित करेगा कि उसके माता-पिता के थ्रेडिंग संदर्भ परमिट होने पर उसे सदस्यता समाप्त करने की आवश्यकता होगी।जब आपका अंतिम ग्राहक छोड़ देता है तो आपकी प्रॉक्सी ऑब्जेक्ट अनसब्सक्राइब करना अच्छा लगेगा, लेकिन यदि कोई नया ग्राहक शामिल हो सकता है और इसकी सदस्यता तुरंत वैध होने की उम्मीद कर सकती है, तो किसी को भी प्रॉक्सी सब्सक्राइब करना पड़ सकता है जब तक कि कोई भी इंटरमीडिएट का संदर्भ रखता हो पर्यवेक्षक जिसका उपयोग नई सदस्यता का अनुरोध करने के लिए किया जा सकता है।

+0

दिलचस्प पढ़ा! हालांकि, मेरी माफ़ी, मेरा प्रश्न भ्रामक हो सकता है, मैं अपने स्वयं के IObservables को लागू करने के बारे में नहीं पूछ रहा था, लेकिन बस सिस्टम द्वारा प्रदान किए गए लोगों का उपयोग कर रहा था। प्रतिक्रियाशील। लिंक्स एक्सटेंशन विधियों और वे स्मृति में अपस्ट्रीम/डाउनस्ट्रीम अवलोकन कैसे रूट करते हैं। – Tyson

1

मुझे लगता है मैं गिदोन के जवाब की मदद से निष्कर्ष पर आते हैं और नीचे एक नमूना Where विधि को तोड़ने की है: करने के लिए

मैं गलत तरीके से मान लिया है कि प्रत्येक बहाव IObservable हर समय नदी के ऊपर से संदर्भित किया गया था (आवश्यकता होने पर घटनाओं को दबाएं)। लेकिन यह अपस्ट्रीम के जीवनकाल के लिए स्मृति में डाउनस्ट्रीम रूट करेगा।

वास्तव में, प्रत्येक अपस्ट्रीम IObservable को डाउनस्ट्रीम IObservable (प्रतीक्षा, IObserver आवश्यक होने पर तैयार करने के लिए तैयार) द्वारा संदर्भित किया गया है। जब तक डाउनस्ट्रीम का संदर्भ दिया जाता है तब तक यह जड़ें स्मृति में अपस्ट्रीम होती हैं (जो समझ में आता है, जबकि डाउनस्ट्रीम अभी भी संदर्भित है, किसी भी समय सदस्यता हो सकती है)।

हालांकि जब कोई सदस्यता होती है, तो डाउनस्ट्रीम संदर्भ श्रृंखला के लिए यह अपस्ट्रीम बनता है, लेकिन केवल IDisposable कार्यान्वयन ऑब्जेक्ट्स पर जो प्रत्येक अवलोकन चरण में सदस्यता प्रबंधित करता है, और केवल उस सदस्यता के जीवनकाल के लिए। (जो भी समझ में आता है - एक सदस्यता मौजूद होने पर, प्रत्येक अपस्ट्रीम 'प्रसंस्करण तर्क' को अंतिम ग्राहक IObserver तक पहुंचने के लिए पारित होने वाली घटनाओं को संभालने के लिए स्मृति में अभी भी होना चाहिए)।

यह दोनों समस्याओं का समाधान देता है - जबकि एक IObservable संदर्भित है, यह सब स्रोत (नदी के ऊपर) स्मृति में IObservables एक सदस्यता के लिए तैयार आयोजन करेगा। और जब एक सब्सक्रिप्शन मौजूद है, तो यह सभी डाउनस्ट्रीम सब्सक्रिप्शन को मेमोरी में रखेगा, जिससे अंतिम सदस्यता अभी भी घटनाओं को प्राप्त करने की इजाजत देगी, भले ही इसका स्रोत IObservable का संदर्भ नहीं दिया जा सके।

मेरे प्रश्न में मेरे उदाहरण में इसे लागू करने के लिए, Where और Cast डाउनस्ट्रीम अवलोकन बहुत कम रहते हैं - Subscribe(observer) कॉल पूर्ण होने तक संदर्भित किया जाता है। वे तब एकत्र किए जाने के लिए स्वतंत्र हैं। तथ्य यह है कि मध्यवर्ती अवलोकनों को अब एकत्रित किया जा सकता है, सब्सक्रिप्शन के लिए कोई समस्या नहीं पैदा होती है, क्योंकि इसने अपनी सदस्यता ऑब्जेक्ट श्रृंखला (अपस्ट्रीम -> डाउनस्ट्रीम) बनाई है जो स्रोत eventStream स्रोत द्वारा रूट किया गया है। जैसे ही प्रत्येक डाउनस्ट्रीम चरण IDisposable सदस्यता ट्रैकर का निपटारा करता है, इस श्रृंखला को रिलीज़ किया जाएगा।

+0

यह सही दिखता है। इंगित करने के लायक एक मामूली विस्तार यह है कि न तो 'कहां' और न ही 'कास्ट' एक 'आईडीस्पोजेबल' ऑब्जेक्ट बनाते हैं, वे IObservable \ IObserver ऑब्जेक्ट्स (Observable.Create जैसे कुछ के माध्यम से) बनाते हैं। केवल एक सदस्यता वास्तव में बनाई गई है (आपके मामले में ईवेंटस्ट्रीम के लिए), अन्यथा सभी अवलोकनों को आसपास रहना होगा। –

+0

हां, लेकिन कास्ट द्वारा लौटे अवलोकन पर सदस्यता कॉल के साइड इफेक्ट के रूप में, यह चेन को इसके माता-पिता को सब्सक्राइब करता है जहां देखा जा सकता है। हां, उपयोगकर्ता-कोड केवल 'देखता है' एक सब्सक्राइब कॉल और एक आईडीस्पोजेबल है, लेकिन उसके अंदर माता-पिता के लिए एक और सदस्यता है, और उसके अंदर एक और, और श्रृंखला को जड़ तक आगे बढ़ाएं। सही बात? – Tyson

+0

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

1

आपको याद रखना होगा कि IObserable<T> (जैसे IEnumerable<T>) आलसी सूचियां हैं। वे तब तक अस्तित्व में नहीं होते जब तक कि कोई व्यक्ति सब्सक्राइब करने या पुनरावृत्ति करके तत्वों तक पहुंचने का प्रयास नहीं करता।

जब आप list.Where(x => x > 0) लिखते हैं तो आप एक नई सूची नहीं बना रहे हैं, आप केवल यह परिभाषित कर रहे हैं कि कोई नई तत्व कैसा दिखाई देगी यदि कोई तत्व तत्वों तक पहुंचने का प्रयास करता है।

यह एक बहुत ही महत्वपूर्ण भेद है।

आप विचार कर सकते हैं कि दो अलग-अलग IObservables हैं। एक परिभाषा और सब्सक्राइब किए गए उदाहरण हैं।

IObservable परिभाषाएं कोई स्मृति के बगल में उपयोग नहीं करती हैं। संदर्भों को स्वतंत्र रूप से साझा किया जा सकता है। वे साफ कचरा इकट्ठा किया जाएगा।

सब्सक्राइब किए गए उदाहरण केवल तभी मौजूद हैं जब कोई सदस्यता लेता है। वे काफी स्मृति का उपयोग कर सकते हैं। जब तक आप .Publish एक्सटेंशन का उपयोग नहीं करते हैं, तो आप संदर्भ साझा नहीं कर सकते हैं। जब सदस्यता समाप्त होती है या .Dispose() पर कॉल करके समाप्त हो जाती है तो स्मृति साफ़ हो जाती है।

सब्सक्राइब किए गए उदाहरणों का एक नया सेट हर नई सदस्यता के लिए बनाया गया है। जब अंतिम बाल सदस्यता का निपटारा किया जाता है तो पूरी श्रृंखला का निपटारा किया जाता है। उन्हें साझा नहीं किया जा सकता है। यदि कोई दूसरी सदस्यता है तो सब्सक्राइब किए गए उदाहरणों की पूरी श्रृंखला बनाई गई है, पहले से स्वतंत्र।

मुझे उम्मीद है कि इससे मदद मिलती है।

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