क्यों रद्द करना रद्द करना रद्द करने से अलग है


137

मैं एक तर्क की तलाश में हूं कि क्यों क्लास के CancellationTokenअलावा .NET स्ट्रक्चर को पेश किया गया CancellationTokenSource। मैं समझता हूं कि एपीआई का उपयोग कैसे किया जाता है, लेकिन यह भी समझना चाहता हूं कि इसे इस तरह से क्यों बनाया गया है।

यानी, हमारे पास क्यों है:

var cts = new CancellationTokenSource();
SomeCancellableOperation(cts.Token);

...
public void SomeCancellableOperation(CancellationToken token) {
    ...
    token.ThrowIfCancellationRequested();
    ...
}

इसके बजाय सीधे CancellationTokenSourceचारों ओर से गुजरने की तरह:

var cts = new CancellationTokenSource();
SomeCancellableOperation(cts);

...
public void SomeCancellableOperation(CancellationTokenSource cts) {
    ...
    cts.ThrowIfCancellationRequested();
    ...
}

क्या यह एक प्रदर्शन अनुकूलन है जो इस तथ्य पर आधारित है कि रद्द करने की स्थिति चेक टोकन के पास होने की तुलना में अधिक बार होती है?

इतना है कि CancellationTokenSourceऔर अद्यतन का ट्रैक रख सकते हैं CancellationTokens, और प्रत्येक टोकन रद्द करने के लिए एक स्थानीय क्षेत्र का उपयोग है?

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

धन्यवाद!

जवाबों:


110

मैं इन वर्गों के डिजाइन और कार्यान्वयन में शामिल था।

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

यदि एपीआई को दुर्व्यवहार करने के लिए दरवाजा खुला छोड़ दिया गया था, तो रद्दीकरण डिजाइन की उपयोगिता जल्दी से मिट सकती है।

CancellationTokenSource == "रद्दीकरण ट्रिगर", प्लस जुड़े श्रोताओं को उत्पन्न करता है

CancellationToken == "रद्दीकरण श्रोता"


7
आपका बहुत बहुत धन्यवाद! अंदर का ज्ञान वाकई काबिले तारीफ है।
एंड्री टारनटोसव

stackoverflow.com/questions/39077497/… क्या आपको पता है कि स्ट्रीमस्टॉक के इनपुटस्ट्रीम के लिए डिफ़ॉल्ट टाइमआउट क्या होना चाहिए? जब मैं पढ़ने के संचालन को रद्द करने के लिए रद्दीकरण का उपयोग करता हूं, तो यह संबंधित सॉकेट को बंद कर देता है। क्या इस मुद्दे को दूर करने का कोई तरीका हो सकता है?
sam18

@ माइक - बस जिज्ञासु: कैसे आप एक सीटी पर एक सीटीएस पर ThrowIfCancellationRequested () की तरह कुछ नहीं कह सकते हैं?
rory.ap

उनके पास अलग-अलग जिम्मेदारियां हैं और इसके लिए cts.Token.ThrowIfCancellationRequested () लिखने की क्रिया भी नहीं है, इसलिए हमने श्रोता एपीआई को सीधे सीटीएस पर नहीं जोड़ा।
माइक लिडेल

@ माइक क्यों रद्दीकरण एक संरचना है और वर्ग नहीं है? मैं किसी भी प्रदर्शन लाभ नहीं देखता
Neir0

85

मेरे पास सटीक सवाल था और मैं इस डिजाइन के पीछे के तर्क को समझना चाहता था।

स्वीकृत उत्तर को औचित्य सही मिला। यहां उस टीम की पुष्टि की गई है जिसने इस सुविधा को बनाया है (जोर मेरा)

दो नए प्रकार ढांचे का आधार बनाते हैं: ए CancellationTokenएक संरचना है जो 'रद्द करने के संभावित अनुरोध' का प्रतिनिधित्व करती है। इस संरचना को एक पैरामीटर के रूप में विधि कॉल में पारित किया जाता है और विधि उस पर सर्वेक्षण कर सकती है या रद्द होने का अनुरोध करने पर निकाल दिया जा सकता है। ए एक CancellationTokenSourceऐसा वर्ग है जो निरस्तीकरण अनुरोध शुरू करने के लिए तंत्र प्रदान करता है और इसके पास एक Token संबद्ध टोकन प्राप्त करने के लिए एक संपत्ति होती है। इन दो वर्गों को एक में जोड़ना स्वाभाविक था, लेकिन यह डिज़ाइन दो प्रमुख संचालन (रद्द करने के अनुरोध को रद्द करने और अवलोकन को रद्द करने के लिए प्रतिक्रिया देते हुए) को सफाई से अलग करने की अनुमति देता है। विशेष रूप से, विधियाँ जो केवल CancellationTokenएक रद्द करने के अनुरोध को मान सकती हैं, लेकिन किसी को आरंभ नहीं कर सकती हैं।

लिंक: .NET 4 कैंसिलेशन फ्रेमवर्क

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

मुझे यह भी लगता है कि यह एपीआई क्लीनर बनाता है और आकस्मिक गलती से बचा जाता है और बेहतर घटक डिजाइन को बढ़ावा देता है।

आइए इन दोनों वर्गों के लिए सार्वजनिक एपीआई देखें।

रद्द करने के लिए एपीआई

रद्दीकरण TokenSource एपीआई

यदि आप उन्हें संयोजित करने के लिए थे, तो LongRunningFunction लिखते समय, मुझे 'रद्द' के उन एकाधिक अधिभार जैसे तरीके दिखाई देंगे, जिनका मुझे उपयोग नहीं करना चाहिए। निजी तौर पर, मुझे डिस्पोज़ विधि को भी देखने से नफरत है।

मुझे लगता है कि वर्तमान वर्ग डिजाइन 'सफलता के गड्ढे' दर्शन का अनुसरण करता है, यह डेवलपर्स को बेहतर घटक बनाने के लिए मार्गदर्शन करता है जो Taskरद्द करने के लिए संभाल सकते हैं और फिर उन्हें जटिल वर्कफ़्लोज़ बनाने के लिए कई तरीकों से एक साथ लिख सकते हैं।

मुझे आपसे एक सवाल पूछना है, क्या आपने सोचा है कि टोकन का उद्देश्य क्या है। यह मेरे लिए कोई मतलब नहीं था। और फिर मैंने मैनेज थ्रेड्स में कैंसेलेशन पढ़ा और सब कुछ क्रिस्टल क्लियर हो गया।

मेरा मानना ​​है कि TPL में रद्दीकरण फ्रेमवर्क डिज़ाइन बिल्कुल शीर्ष पर है।


2
यदि आप आश्चर्य करते हैं कि CancellationTokenSourceवास्तव में यह टोकन से जुड़ा होने पर रद्द करने का अनुरोध कैसे कर सकता है (टोकन इसे स्वयं नहीं कर सकता है): कैंसिलेशनटोकन में यह आंतरिक निर्माता है: internal CancellationToken(CancellationTokenSource source) { this.m_source = source; }और यह गुण: public bool IsCancellationRequested { get { return this.m_source != null && this.m_source.IsCancellationRequested; } }कैंसेलेशनटोकनसेन आंतरिक निर्माता का उपयोग करता है, इसलिए टोकन का संदर्भ है source (m_source)
chviLadislav

65

वे तकनीकी कारणों से नहीं बल्कि शब्दार्थ से अलग हैं। यदि आप CancellationTokenILSpy के तहत कार्यान्वयन को देखते हैं, तो आप पाएंगे कि यह केवल एक आवरण के आसपास है CancellationTokenSource(और इस तरह एक संदर्भ से गुजरने की तुलना में कोई अलग प्रदर्शन नहीं है)।

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


धन्यवाद! मेरी पलक प्रतिक्रिया है कि, शब्दार्थ, यह एक प्रश्नात्मक दृष्टिकोण है, IReadOnlyList उपलब्ध कराने के साथ। यह बहुत प्रशंसनीय ध्वनि करता है, हालांकि, इसलिए आपके उत्तर को स्वीकार करता है।
एंड्रे टारनटोसव

1
आगे की सोच पर, मैं अपने आप को प्रशंसनीयता पर सवाल उठाता हूँ। क्या यह अधिक समझ में नहीं आएगा, फिर, ICancellationToken इंटरफ़ेस प्रदान करने के लिए जिसे रद्द करना TokenSource लागू होगा? मैं चाहता हूं कि .NET टीम का कोई व्यक्ति इसमें झंकार करे।
एंड्री टारनटोसव

यह अभी भी एक चालाक प्रोग्रामर के लिए कास्टिंग में परिणाम हो सकता है CancellationTokenSource। आपको लगता है कि आप बस "ऐसा मत करो" कह सकते हैं, लेकिन लोग (मेरे सहित!) कभी-कभी इन चीजों को कुछ छिपी हुई कार्यक्षमता पर प्राप्त करने के लिए करते हैं, और ऐसा होगा। यह मेरा वर्तमान सिद्धांत है, कम से कम।
कोरी नेल्सन

1
यह नहीं है कि आप ज्यादातर मामलों में एपीआई कैसे डिज़ाइन करते हैं, जब तक कि यह सुरक्षा से संबंधित न हो। मैं कुछ अन्य स्पष्टीकरण पर शर्त लगाऊंगा, जैसे "भविष्य में प्रदर्शन अनुकूलन के लिए उनके विकल्प खुले रखना"। लेकिन फिर भी, तुम्हारा अब तक का सबसे अच्छा है।
एंड्री टारनटोसव

अरे, मुझे आशा है कि आप मुझे कार्यान्वयन में शामिल व्यक्ति के लिए स्वीकृत उत्तर को फिर से सौंप देंगे। जबकि आप दोनों एक ही बात कहते हैं, मुझे लगता है कि शीर्ष पर होने वाले निश्चित उत्तर से एसओ को लाभ होता है।
एंड्री टारनटोसव

10

यह CancellationTokenएक संरचना है इसलिए कई प्रतियाँ इसे विधियों के साथ पारित करने के कारण मौजूद हो सकती हैं।

CancellationTokenSourceसेट एक टोकन की सभी प्रतियों जब बुलाने की राज्य Cancelस्रोत पर। इस MSDN पृष्ठ को देखें

डिजाइन का कारण सिर्फ चिंताओं को अलग करने और एक संरचना की गति का मामला हो सकता है।


1
धन्यवाद। ध्यान दें कि एक अन्य उत्तर बताता है कि तकनीकी रूप से (आईएल में), कैंसेलेशनटोकनसेन स्रोत टोकन की स्थिति निर्धारित नहीं करता है; इसके बजाय, टोकन रद्द करने के लिए एक वास्तविक संदर्भ को लपेटता है। स्रोत, और बस इसे रद्द करने के लिए जाँच करें। कुछ भी हो, गति केवल यहाँ खो सकती है।
एंड्री टारनटोसव

+1। मुझे समझ नहीं आ रहा है। यदि इसका मान प्रकार है तो प्रत्येक विधि का अपना मूल्य (अलग मूल्य) है। तो कैसे एक विधि जानता है कि अगर एक रद्द बुलाया गया है? यह टोकनस्रोत का कोई संदर्भ नहीं है। सभी विधि देख सकते हैं "5" की तरह एक स्थानीय मूल्य प्रकार है। क्या तुम समझा सकते हो ?
रॉय नमिर

1
@RoyiNamir: प्रत्येक कैंसिलेशनटोकन का निजी संदर्भ "m_source" प्रकार का रद्द करने के लिए है
TokenSource

2

वह CancellationTokenSourcefor बात ’है जो निरस्तीकरण जारी करती है, जो भी कारण हो। इसे atch प्रेषण ’के लिए एक रास्ता चाहिए जो CancellationTokenइसे जारी किए गए सभी को रद्द कर दे। उदाहरण के लिए, ASP.NET किसी अनुरोध के निरस्त होने पर परिचालन रद्द कर सकता है। प्रत्येक अनुरोध में CancellationTokenSourceयह है कि जारी किए गए सभी टोकन को रद्द कर दिया जाए।

बीटीडब्ल्यू के परीक्षण के लिए यह महान है - अपना स्वयं का रद्दीकरण टोकन स्रोत बनाएं, एक टोकन प्राप्त करें Cancel, स्रोत पर कॉल करें , और अपने कोड को टोकन पास करें जिसे रद्दीकरण को संभालना है।


धन्यवाद - लेकिन दोनों जिम्मेदारियों (जो कर रहे हैं बहुत से संबंधित) एक ही कक्षा के लिए दिया जा सकता है। वास्तव में व्याख्या नहीं करता कि वे अलग क्यों हैं।
एंड्रे टारनटसोव
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.