वैल्यू टस्क <T> C # में टास्क <T> का उपयोग क्यों करेगा?


168

C # 7.0 के रूप में async विधियाँ ValueTask <T> लौटा सकती हैं। स्पष्टीकरण कहता है कि इसका उपयोग तब किया जाना चाहिए जब हमारे पास कैश्ड परिणाम हो या सिंक्रोनस कोड के माध्यम से एसिंक्स का अनुकरण करना हो। हालाँकि मुझे अभी भी समझ में नहीं आ रहा है कि ValueTask का उपयोग करने में क्या समस्या है या वास्तव में क्यों async / wait को प्रारंभ से मान प्रकार के साथ नहीं बनाया गया था। जब ValueTask काम करने में विफल हो जाएगा?


7
मुझे लगता है यह के लाभ के साथ क्या करना है ValueTask<T>(आवंटन के संदर्भ में) के संचालन कर रहे हैं के लिए सफल नहीं वास्तव में अतुल्यकालिक (क्योंकि उस मामले में ValueTask<T>अभी भी ढेर आवंटन आवश्यकता होगी)। Task<T>पुस्तकालयों के भीतर बहुत सारे अन्य समर्थन होने की बात भी है ।
जॉन स्कीट

3
@JonSkeet मौजूदा लाइब्रेरी एक समस्या है लेकिन यह सवाल है कि टास्क शुरू से ही वैल्यूटैस्क होना चाहिए था? वास्तविक async सामान के लिए इसका उपयोग करने पर लाभ मौजूद नहीं हो सकता है लेकिन क्या यह हानिकारक है?
स्टिलगर

8
अधिक ज्ञान के लिए github.com/dotnet/corefx/issues/4708#issuecomment-160658188 देखें, जितना मैं बता पाऊंगा :)
जॉन स्कीट


3
@JoelMueller प्लॉट को मोटा करता है :)
Stilgar

जवाबों:


245

से एपीआई डॉक्स (जोर जोड़ा):

विधियाँ इस मान प्रकार का एक उदाहरण लौटा सकती हैं, जब यह संभावना है कि उनके संचालन का परिणाम तुल्यकालिक रूप से उपलब्ध होगा और जब विधि को इतनी बार लागू किए जाने की उम्मीद है, तो Task<TResult>प्रत्येक कॉल के लिए एक नया आवंटित करने की लागत निषेधात्मक होगी।

ValueTask<TResult>इसके बजाय का उपयोग करने के लिए tradeoffs हैं Task<TResult>। उदाहरण के लिए, जबकि एक ValueTask<TResult>मामले में आवंटन से बचने में मदद मिल सकती है जहां सफल परिणाम समकालिक रूप से उपलब्ध है, इसमें दो फ़ील्ड भी शामिल हैं जबकि Task<TResult>एक संदर्भ प्रकार एक एकल फ़ील्ड है। इसका मतलब यह है कि एक विधि कॉल एक के बजाय डेटा के लायक दो क्षेत्रों को समाप्त करती है, जो कॉपी करने के लिए अधिक डेटा है। इसका अर्थ यह भी है कि यदि कोई विधि जो इनमें से किसी एक को लौटाती है async, एक विधि के भीतर प्रतीक्षित होती है, तो उस asyncविधि के लिए राज्य मशीन उस संरचना को संग्रहीत करने की आवश्यकता के कारण बड़ी हो जाएगी जो एकल संदर्भ के बजाय दो क्षेत्रों की होती है।

इसके अलावा, के माध्यम से एक अतुल्यकालिक आपरेशन के परिणाम लेने के अलावा अन्य उपयोगों के लिए await, ValueTask<TResult>एक और अधिक घुमावदार प्रोग्रामिंग मॉडल, जिसमें वास्तव में बारी अधिक आवंटन को जन्म दे सकता हो सकता है। उदाहरण के लिए, एक ऐसी विधि पर विचार करें जो Task<TResult>एक सामान्य परिणाम के रूप में कैश्ड कार्य के साथ वापस आ सकती है या ए ValueTask<TResult>। यदि परिणाम का उपभोक्ता इसे एक के रूप में उपयोग करना चाहता है Task<TResult>, जैसे कि तरीकों में Task.WhenAllऔर के साथ उपयोग करने के लिए Task.WhenAny, ValueTask<TResult>पहले इसे एक Task<TResult>उपयोग में परिवर्तित करने की आवश्यकता होगी AsTask, जो एक आवंटन की ओर जाता है जो एक कैशेड Task<TResult>उपयोग किए जाने से बचा जाता था। पहली जगह में।

इस प्रकार, किसी भी अतुल्यकालिक विधि के लिए डिफ़ॉल्ट विकल्प को वापस करना चाहिए Taskया Task<TResult>। केवल अगर प्रदर्शन विश्लेषण यह साबित करता है कि ValueTask<TResult>इसके बजाय इसका उपयोग किया जाना चाहिए Task<TResult>


7
@MattThomas: यह एक एकल Taskआवंटन (जो इन दिनों छोटा और सस्ता है) को बचाता है , लेकिन कॉलर के मौजूदा आवंटन को बड़ा बनाने और रिटर्न वैल्यू के आकार को दोगुना करने (रजिस्टर आवंटन को प्रभावित करने) की कीमत पर। हालांकि यह एक बफ़र्ड पठन परिदृश्य के लिए एक स्पष्ट विकल्प है, इसे डिफ़ॉल्ट रूप से सभी इंटरफेस पर लागू करना कुछ ऐसा नहीं है जिसे मैं सुझाऊंगा।
स्टीफन क्लीयर

1
ठीक है, या तो Taskया ValueTaskएक तुल्यकालिक वापसी प्रकार (के साथ के रूप में इस्तेमाल किया जा सकता Task.FromResult)। लेकिन अभी भी मूल्य (हेह) है ValueTaskअगर आपके पास कुछ है जो आप तुल्यकालिक होने की उम्मीद करते हैं। ReadByteAsyncएक उत्कृष्ट उदाहरण है। मेरा मानना ​​है कि ValueTask मुख्य रूप से नए "चैनल" (निम्न-स्तरीय बाइट स्ट्रीम) के लिए बनाया गया था, संभवतः ASP.NET कोर में भी उपयोग किया जाता है जहां प्रदर्शन वास्तव में मायने रखता है।
स्टीफन क्ली

1
ओह, मुझे पता है कि लोल, बस सोच रहा था कि क्या आपके पास उस विशिष्ट टिप्पणी को जोड़ने के लिए कुछ है?)
जुलेगलन

2
क्या यह PR वैल्यूटैस्क को तरजीह देने के लिए शेष राशि को स्विच करता है ? (Ref: blog.marcgravell.com/2019/08/… )
स्टुअर्ट

2
@stuartd: अभी के लिए, मैं अभी भी Task<T>डिफ़ॉल्ट के रूप में उपयोग करने की सलाह दूंगा । यह केवल इसलिए है क्योंकि अधिकांश डेवलपर्स आस-पास के प्रतिबंधों से परिचित नहीं हैं ValueTask<T>(विशेष रूप से, "केवल एक बार उपभोग करते हैं" नियम और "कोई अवरुद्ध नहीं" नियम)। उस ने कहा, यदि आपकी टीम के सभी देव अच्छे हैं ValueTask<T>, तो मैं टीम-स्तर के दिशानिर्देश को प्राथमिकता देने की सिफारिश करूंगा ValueTask<T>
स्टीफन क्लीयर

104

हालाँकि मुझे अभी भी समझ में नहीं आ रहा है कि हमेशा ValueTask का उपयोग करने में क्या समस्या है

संरचना प्रकार स्वतंत्र नहीं हैं। एक संदर्भ के आकार की तुलना में बड़े होते हैं नकल संरचना एक संदर्भ की नकल की तुलना में धीमी हो सकती है। एक संदर्भ की तुलना में बड़े स्टोरेज स्ट्रक्चर एक संदर्भ को संग्रहीत करने की तुलना में अधिक मेमोरी लेते हैं। 64 बिट्स से बड़े स्ट्रक्चर को तब रेफर नहीं किया जा सकता है जब किसी रेफरेंस को एनलेगर किया जा सकता है। कम संग्रह दबाव का लाभ लागत से अधिक नहीं हो सकता है।

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

क्यों async / इंतजार शुरू से एक मूल्य प्रकार के साथ नहीं बनाया गया था।

awaitTask<T>पहले से मौजूद प्रकार के बाद सी # में जोड़ा गया था। यह एक नए प्रकार का आविष्कार करने के लिए कुछ हद तक विकृत होगा जब पहले से ही मौजूद था। और await2012 में भेजे गए एक पर बसने से पहले एक महान कई पुनरावृत्तियों के माध्यम से चला गया। सही अच्छा का दुश्मन है; किसी ऐसे समाधान को जहाज करने के लिए बेहतर है जो मौजूदा बुनियादी ढांचे के साथ अच्छी तरह से काम करता है और फिर यदि उपयोगकर्ता की मांग है, तो बाद में सुधार प्रदान करें।

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

क्या कोई समझा सकता है जब ValueTask काम करने में विफल होगा?

बात का उद्देश्य बेहतर प्रदर्शन है। यह काम नहीं करता है अगर यह औसत रूप से और महत्वपूर्ण रूप से प्रदर्शन में सुधार नहीं करता है । इसकी कोई गारंटी नहीं है कि यह होगा।


23

ValueTask<T>का सबसेट नहीं है Task<T>, यह एक सुपरसेट है

ValueTask<T>एक T और a का एक विभेदित संघ है Task<T>, जो इसे ReadAsync<T>उपलब्ध मूल्य के समतुल्य रूप से T मान प्रदान करने के लिए आवंटन-मुक्त बनाता है (उपयोग करने के विपरीत Task.FromResult<T>, जिसे एक Task<T>उदाहरण आवंटित करने की आवश्यकता है )। ValueTask<T>प्रतीक्षा योग्य है, इसलिए उदाहरणों की सबसे अधिक खपत एक के साथ अप्रभेद्य होगी Task<T>

ValueTask, एक संरचना होने के नाते, एसिंक्स विधियों को लिखने में सक्षम बनाता है जो मेमोरी को आवंटित नहीं करते हैं जब वे एपीआई संगतता से समझौता किए बिना सिंक्रनाइज़ करते हैं। एक टास्क रिटर्निंग विधि के साथ एक इंटरफ़ेस होने की कल्पना करें। इस इंटरफ़ेस को लागू करने वाले प्रत्येक वर्ग को एक टास्क वापस करना होगा, भले ही वे सिंक्रोनाइज़ करने के लिए हों (उम्मीद है कि Task.FromResult का उपयोग करके)। आप निश्चित रूप से इंटरफ़ेस पर 2 अलग-अलग तरीके, एक तुल्यकालिक एक और एक async एक कर सकते हैं लेकिन इसके लिए "सिंक पर सिंक" और "सिंक पर सिंक" से बचने के लिए 2 अलग-अलग कार्यान्वयन की आवश्यकता होती है।

तो यह आपको एक विधि लिखने की अनुमति देता है जो या तो अतुल्यकालिक या तुल्यकालिक है, बल्कि फिर प्रत्येक के लिए एक अन्यथा समान विधि लिखना है। आप इसे कहीं भी उपयोग कर सकते हैं, Task<T>लेकिन यह अक्सर कुछ भी नहीं जोड़ रहा है।

खैर, यह एक बात जोड़ता है: यह कॉलर को एक निहित वादा जोड़ता है कि विधि वास्तव में ValueTask<T>प्रदान करने वाली अतिरिक्त कार्यक्षमता का उपयोग करती है। मैं व्यक्तिगत रूप से पैरामीटर और रिटर्न प्रकार चुनना पसंद करता हूं जो कॉलर को जितना संभव हो उतना बता सकता है। IList<T>यदि गणना गणना प्रदान नहीं कर सकती है, तो वापस न करें ; IEnumerable<T>अगर यह कर सकते हैं तो वापस न करें । आपके उपभोक्ताओं को यह जानने के लिए कोई दस्तावेज़ नहीं देखना चाहिए कि आपके कौन से तरीके यथोचित रूप से समकालिक कहे जा सकते हैं और कौन से नहीं।

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

यह अनिवार्य रूप से मजबूत टाइपिंग के लिए है।

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

अगर विधि लिखने वाला आदमी यह नहीं जानता कि क्या इसे सिंक्रोनाइज़ कहा जा सकता है, तो पृथ्वी पर कौन है ?!

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

यहाँ समस्या आपकी वापसी प्रकारों की नहीं है, यह प्रोग्रामर की भूमिका में डाले जा रहे हैं जो वे तैयार नहीं हैं। यह एक कारण से हुआ होगा, इसलिए मुझे यकीन है कि इसे ठीक करने के लिए तुच्छ नहीं किया जा सकता है। इसका वर्णन निश्चित रूप से एक समाधान नहीं है। लेकिन संकलक के अतीत की समस्या को हल करने के लिए एक रास्ता की तलाश एक समाधान भी नहीं है।


4
यदि मुझे कोई विधि वापस लौटती दिखाई देती है ValueTask<T>, तो मुझे लगता है कि जिस व्यक्ति ने विधि लिखी थी, उसने ऐसा इसलिए किया क्योंकि विधि वास्तव में उस कार्यक्षमता का उपयोग करती है जो ValueTask<T>जोड़ती है। मुझे समझ में नहीं आता है कि आपको क्यों लगता है कि आपके सभी तरीकों के लिए एक ही रिटर्न टाइप करना वांछनीय है। वहाँ क्या लक्ष्य है?
15ee8f99-57ff-4f92-890c-b56153

2
लक्ष्य 1 कार्यान्वयन को बदलने की अनुमति देने के लिए होगा। क्या होगा यदि मैं अभी परिणाम को कैशिंग नहीं कर रहा हूं, लेकिन बाद में कैशिंग कोड जोड़ें? लक्ष्य 2 एक टीम के लिए एक स्पष्ट दिशानिर्देश होगा, जहां सभी सदस्य यह नहीं समझते हैं कि ValueTask उपयोगी है। बस इसे हमेशा करें अगर इसका इस्तेमाल करने में कोई बुराई नहीं है।
स्टिलगर

6
मेरे विचार में, यह लगभग आपके सभी तरीकों की वापसी की तरह है objectक्योंकि हो सकता है कि किसी दिन आप उन्हें intइसके बजाय वापस लौटना चाहें string
15ee8f99-57ff-4f92-890c-b56153

3
हो सकता है लेकिन अगर आप यह सवाल पूछते हैं कि "आप वस्तु के बजाय कभी स्ट्रिंग क्यों लौटाएंगे" तो मैं आसानी से इसका उत्तर टाइप सुरक्षा की कमी की ओर इशारा कर सकता हूं। हर जगह ValueTask का उपयोग नहीं करने के कारण बहुत अधिक सूक्ष्म प्रतीत होते हैं।
स्टिलगर

3
@ स्टिलगर एक बात मैं कहूंगा: सूक्ष्म समस्याओं को आपको स्पष्ट लोगों की तुलना में अधिक चिंता करनी चाहिए।
15ee8f99-57ff-4f92-890c-b56153

20

.Net Core 2.1 में कुछ बदलाव हैं । .Net कोर 2.1 से शुरू करें ValueTask न केवल सिंक्रोनस पूरा किए गए कार्यों का प्रतिनिधित्व कर सकता है, बल्कि एसिंक्स भी पूरा कर सकता है। इसके अलावा हम गैर-सामान्य ValueTaskप्रकार प्राप्त करते हैं ।

मैं स्टीफन Toub टिप्पणी छोड़ दूंगा जो आपके प्रश्न से संबंधित है:

हमें अभी भी मार्गदर्शन को औपचारिक बनाने की आवश्यकता है, लेकिन मुझे उम्मीद है कि यह सार्वजनिक एपीआई सतह क्षेत्र के लिए कुछ इस तरह होगा:

  • टास्क सबसे अधिक प्रयोज्य प्रदान करता है।

  • ValueTask प्रदर्शन अनुकूलन के लिए सबसे अधिक विकल्प प्रदान करता है।

  • यदि आप एक इंटरफ़ेस / वर्चुअल विधि लिख रहे हैं जिसे अन्य लोग ओवरराइड करेंगे, तो ValueTask सही डिफ़ॉल्ट विकल्प है।

  • यदि आप उम्मीद करते हैं कि एपीआई गर्म रास्तों पर उपयोग किया जाएगा जहां आवंटन मायने रखेगा, तो ValueTask एक अच्छा विकल्प है।

  • अन्यथा, जहां कार्य महत्वपूर्ण नहीं है, टास्क के लिए डिफ़ॉल्ट, क्योंकि यह बेहतर गारंटी और उपयोगिता प्रदान करता है।

कार्यान्वयन के दृष्टिकोण से, लौटे ValueTask के कई उदाहरण अभी भी टास्क द्वारा समर्थित होंगे।

फ़ीचर का उपयोग न केवल .net कोर 2.1 में किया जा सकता है। आप इसे System.Threading.Tasks.Extensions पैकेज के साथ उपयोग कर पाएंगे ।


1
स्टीफन से अधिक आज: blogs.msdn.microsoft.com/dotnet/2018/11/07/…
माइक चेल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.