सबसे पहले, अपनी तरह के शब्दों के लिए धन्यवाद। यह वास्तव में एक कमाल की विशेषता है और मुझे इसका एक छोटा सा हिस्सा होने की खुशी है।
यदि मेरा सभी कोड धीरे-धीरे async में बदल रहा है, तो बस इसे डिफ़ॉल्ट रूप से सभी async क्यों नहीं बनाते?
खैर, आप अतिशयोक्ति कर रहे हैं; आपका सभी कोड async नहीं बदल रहा है। जब आप एक साथ दो "सादे" पूर्णांक जोड़ते हैं, तो आप परिणाम की प्रतीक्षा नहीं कर रहे हैं। जब आप तीसरे भविष्य के पूर्णांक प्राप्त करने के लिए भविष्य के दो पूर्णांकों को एक साथ जोड़ते हैं - क्योंकि यही है, यह एक पूर्णांक है जिसे आप भविष्य में उपयोग करने जा रहे हैं - निश्चित रूप से आपको परिणाम की प्रतीक्षा होगी।Task<int>
सबकुछ नहीं बनाने का प्राथमिक कारण यह है कि async / प्रतीक्षा का उद्देश्य कई उच्च विलंबता कार्यों के साथ दुनिया में कोड लिखना आसान बनाना है । आपके संचालन का अधिकांश हिस्सा उच्च विलंबता नहीं है, इसलिए यह उस विलंब को कम करने वाले प्रदर्शन को हिट करने के लिए कोई मतलब नहीं रखता है। बल्कि, आपके कुछ महत्वपूर्ण ऑपरेशन उच्च विलंबता वाले होते हैं, और उन ऑपरेशनों के कारण कोड में एसिंक्स की ज़ोंबी इन्फ़ेक्शन होता है।
यदि प्रदर्शन एकमात्र समस्या है, तो निश्चित रूप से कुछ चतुर अनुकूलन ओवरहेड को स्वचालित रूप से हटा सकते हैं जब इसकी आवश्यकता नहीं होती है।
सिद्धांत रूप में, सिद्धांत और व्यवहार समान हैं। व्यवहार में, वे कभी नहीं होते हैं।
इस प्रकार के परिवर्तन के खिलाफ मुझे तीन अंक देने चाहिए, उसके बाद एक अनुकूलन पास।
पहला बिंदु फिर से है: C # / VB / F # में async अनिवार्य रूप से निरंतरता का सीमित रूप है । कार्यात्मक भाषा समुदाय में भारी मात्रा में शोध यह पता लगाने के तरीकों में बदल गया है कि कोड को कैसे अनुकूलित किया जाए जो निरंतर गुजरने वाली शैली का भारी उपयोग करता है। कंपाइलर टीम को संभवतः एक ऐसी दुनिया में समान समस्याओं को हल करना होगा जहां "async" डिफ़ॉल्ट थी और गैर-async विधियों को पहचानना और de-async-ified होना था। C # टीम वास्तव में खुली अनुसंधान समस्याओं को लेने में दिलचस्पी नहीं रखती है, इसलिए यह वहीं के बड़े बिंदु हैं।
इसके खिलाफ एक दूसरा बिंदु यह है कि C # में "रेफ़रेंशियल ट्रांसपेरेंसी" का स्तर नहीं है, जो इन प्रकार के अनुकूलन को और अधिक ट्रैफ़िक बनाता है। "रेफ़रेंशियल ट्रांसपेरेंसी" से मेरा तात्पर्य उस संपत्ति से है जिसका मूल्यांकन करने पर किसी अभिव्यक्ति का मूल्य निर्भर नहीं करता है । जैसी अभिव्यक्तियाँ 2 + 2
प्रासंगिक रूप से पारदर्शी होती हैं; यदि आप चाहते हैं तो आप संकलन समय पर मूल्यांकन कर सकते हैं, या इसे रनटाइम तक स्थगित कर सकते हैं और समान उत्तर प्राप्त कर सकते हैं। लेकिन एक्स और वाई समय के साथ बदल सकते हैंx+y
क्योंकि एक अभिव्यक्ति की तरह समय के आसपास स्थानांतरित नहीं किया जा सकता है ।
जब कोई साइड इफ़ेक्ट होगा, तो इस बारे में Async बहुत कठिन हो जाता है। Async से पहले, अगर आपने कहा:
M();
N();
और M()
था void M() { Q(); R(); }
, और N()
था void N() { S(); T(); }
, और R
और S
उत्पादन साइड इफेक्ट है, तो आप जानते हैं कि आर के पक्ष प्रभाव एस के पक्ष प्रभाव से पहले होता है। लेकिन अगर आपके पास है async void M() { await Q(); R(); }
तो अचानक वह खिड़की से बाहर चला जाता है। आपको इस बात की कोई गारंटी नहीं R()
है कि इससे पहले या बाद में क्या होने वाला है S()
(जब तक कि निश्चित रूप M()
से प्रतीक्षित नहीं है; लेकिन निश्चित रूप से इसकी Task
आवश्यकता का इंतजार नहीं किया जाना चाहिए N()
)।
अब कल्पना करें कि आपके प्रोग्राम में कोड के हर टुकड़े पर लागू होने वाले आदेशों के प्रभाव के बारे में पता नहीं क्या-क्या होता है, सिवाय इसके कि ऑप्टिमाइज़र डी-एसिंक्स-इफी को प्रबंधित करता है। मूल रूप से आपके पास कोई सुराग नहीं है कि किन भावों का मूल्यांकन किस क्रम में किया जाएगा, जिसका अर्थ है कि सभी अभिव्यक्तियों को प्रासंगिक रूप से पारदर्शी होना चाहिए, जो कि C # जैसी भाषा में कठिन है।
इसके खिलाफ एक तीसरा बिंदु यह है कि आपको फिर पूछना होगा "async इतना विशेष क्यों है?" यदि आप यह तर्क देने जा रहे हैं कि हर ऑपरेशन वास्तव में होना चाहिए Task<T>
तो आपको प्रश्न का उत्तर देने में सक्षम होने की आवश्यकता है "क्यों नहीं Lazy<T>
?" या "क्यों नहीं Nullable<T>
?" या "क्यों नहीं IEnumerable<T>
?" क्योंकि हम बस इतनी आसानी से कर सकते थे। ऐसा क्यों नहीं होना चाहिए कि प्रत्येक ऑपरेशन को अशक्त करने के लिए उठाया जाता है ? या प्रत्येक ऑपरेशन को आलसी रूप से गणना की जाती है और परिणाम बाद के लिए कैश किया जाता है , या हर ऑपरेशन का परिणाम केवल एक मूल्य के बजाय मूल्यों का एक क्रम होता है । फिर आपको उन स्थितियों को अनुकूलित करने का प्रयास करना होगा जहां आप जानते हैं कि "ओह, यह कभी भी अशक्त नहीं होना चाहिए, इसलिए मैं बेहतर कोड उत्पन्न कर सकता हूं", और इसी तरह।
इशारा किया जा रहा है: यह मेरे लिए स्पष्ट नहीं है कि Task<T>
वास्तव में यह बहुत काम वारंट है।
यदि इस प्रकार की चीजें आपको रूचि देती हैं, तो मैं आपको हास्केल जैसी कार्यात्मक भाषाओं की जांच करने की सलाह देता हूं, जिनमें बहुत अधिक संदर्भात्मक पारदर्शिता है और सभी प्रकार के आउट-ऑफ-ऑर्डर मूल्यांकन की अनुमति है और स्वचालित कैशिंग करते हैं। हास्केल को "मोनैडिक लिफ्टिंग" के प्रकार के लिए अपने प्रकार की प्रणाली में बहुत अधिक मजबूत समर्थन मिला है, जिसे मैंने देखा है।