यदि async-wait कोई अतिरिक्त थ्रेड नहीं बनाता है, तो यह अनुप्रयोगों को कैसे उत्तरदायी बनाता है?


242

समय और समय फिर से, मैंने कहा कि इसका उपयोग करते हुए async- awaitकोई अतिरिक्त सूत्र नहीं बनाता है। इसका कोई मतलब नहीं है क्योंकि एक ही तरीका है कि एक कंप्यूटर एक बार में 1 से अधिक काम कर सकता है

  • वास्तव में एक समय में 1 से अधिक काम करना (समानांतर में निष्पादित करना, कई प्रोसेसर का उपयोग करना)
  • कार्यों को शेड्यूल करना और उनके बीच स्विच करना (थोड़ा ए, थोड़ा बी, थोड़ा सा ए आदि)।

तो अगर async- awaitउनमें से कोई भी नहीं करता है, तो यह एक आवेदन को कैसे उत्तरदायी बना सकता है? यदि केवल 1 थ्रेड है, तो किसी भी विधि को कॉल करने का अर्थ है कि किसी भी चीज़ को करने से पहले विधि के पूरा होने की प्रतीक्षा करना, और उस विधि के अंदर के तरीकों को आगे बढ़ने से पहले परिणाम के लिए इंतजार करना होगा, और आगे।


17
IO कार्य सीपीयू बाध्य नहीं हैं और इस प्रकार एक थ्रेड की आवश्यकता नहीं है। एसओएन का मुख्य बिंदु आईओ बाउंड कार्यों के दौरान थ्रेड्स को ब्लॉक नहीं करना है।
जुहार

24
@ जेड्वेंग: नहीं, बिल्कुल नहीं। भले ही इसने नए सूत्र बनाए हों , लेकिन यह एक नई प्रक्रिया बनाने से बहुत अलग है।
जॉन स्कीट २४'१६

8
यदि आप कॉलबैक-आधारित एसिंक्रोनस प्रोग्रामिंग को समझते हैं, तो आप समझते हैं कि कैसे await/ asyncबिना किसी थ्रेड को बनाए काम करता है।
user253751

6
यह किसी एप्लिकेशन को अधिक प्रतिक्रियाशील नहीं बनाता है , लेकिन यह आपको अपने थ्रेड्स को अवरुद्ध करने से रोकता है, जो गैर-जिम्मेदार अनुप्रयोगों का एक सामान्य कारण है।
ओवेन

6
@ रबरडक: हाँ, यह निरंतरता के लिए थ्रेड पूल से एक धागे का उपयोग कर सकता है। लेकिन यह उस तरह से एक धागा शुरू नहीं कर रहा है जिस तरह से ओपी यहाँ कल्पना करता है - ऐसा नहीं है कि यह कहता है "इस साधारण तरीके को लें, अब इसे एक अलग धागे में चलाएं - वहाँ, यह async है।" यह उससे बहुत अधिक सूक्ष्म है।
जॉन स्कीट

जवाबों:


299

वास्तव में, async / प्रतीक्षा यह जादुई नहीं है। पूरा विषय काफी व्यापक है लेकिन आपके प्रश्न के त्वरित और अभी तक पूर्ण उत्तर के लिए मुझे लगता है कि हम प्रबंधित कर सकते हैं।

आइए विंडोज फॉर्म एप्लिकेशन में एक साधारण बटन क्लिक ईवेंट से निपटें:

public async void button1_Click(object sender, EventArgs e)
{
    Console.WriteLine("before awaiting");
    await GetSomethingAsync();
    Console.WriteLine("after awaiting");
}

मैं स्पष्ट रूप से इस बारे में बात नहीं करने जा रहा हूं कि यह GetSomethingAsyncअभी के लिए जो भी लौट रहा है। मान लें कि यह कुछ ऐसा है जो 2 सेकंड के बाद पूरा होगा।

एक पारंपरिक, गैर-अतुल्यकालिक, दुनिया में, आपका बटन क्लिक इवेंट हैंडलर कुछ इस तरह दिखाई देगा:

public void button1_Click(object sender, EventArgs e)
{
    Console.WriteLine("before waiting");
    DoSomethingThatTakes2Seconds();
    Console.WriteLine("after waiting");
}

जब आप फॉर्म में बटन पर क्लिक करते हैं, तो एप्लिकेशन लगभग 2 सेकंड के लिए स्थिर हो जाएगा, जबकि हम इस विधि के पूरा होने की प्रतीक्षा करते हैं। क्या होता है कि "संदेश पंप", मूल रूप से एक लूप, अवरुद्ध है।

यह लूप लगातार विंडोज़ पूछता है "क्या किसी ने कुछ किया है, जैसे माउस को स्थानांतरित किया, कुछ पर क्लिक किया? क्या मुझे कुछ पुनरावृत्ति करने की आवश्यकता है? यदि हां, तो मुझे बताएं!" और फिर उस "कुछ" को संसाधित करता है। इस लूप को एक संदेश मिला कि उपयोगकर्ता ने "बटन 1" (या विंडोज से बराबर प्रकार का संदेश) पर क्लिक किया, और button1_Clickऊपर दी गई विधि को समाप्त कर दिया । इस विधि के वापस आने तक, यह लूप अब प्रतीक्षा में अटका हुआ है। इसमें 2 सेकंड का समय लगता है और इस दौरान, कोई भी संदेश संसाधित नहीं किया जा रहा है।

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

इसलिए, यदि पहले उदाहरण में, async/awaitनए धागे नहीं बनाए जाते हैं, तो यह कैसे करता है?

खैर, क्या होता है कि आपकी विधि दो में विभाजित है। यह उन चीजों के व्यापक विषय प्रकारों में से एक है, इसलिए मैं बहुत अधिक विस्तार में नहीं जाऊंगा, लेकिन यह कहने के लिए पर्याप्त है कि विधि इन दो भागों में विभाजित है:

  1. awaitकॉल करने के लिए , सहित सभी कोडGetSomethingAsync
  2. निम्नलिखित सभी कोड await

उदाहरण:

code... code... code... await X(); ... code... code... code...

पुन: व्यवस्थित:

code... code... code... var x = X(); await X; code... code... code...
^                                  ^          ^                     ^
+---- portion 1 -------------------+          +---- portion 2 ------+

मूल रूप से विधि इस तरह से निष्पादित होती है:

  1. यह सब कुछ निष्पादित करता है await
  2. यह GetSomethingAsyncविधि को कॉल करता है, जो अपनी बात करता है, और कुछ ऐसा करता है जो भविष्य में 2 सेकंड पूरा करेगा

    अब तक हम अभी भी मूल कॉल के अंदर हैं बटन1_ क्लिक करें, मुख्य लूप पर हो रहा है, जिसे मैसेज लूप कहा जाता है। यदि कोड को awaitबहुत समय लगता है, तो UI अभी भी स्थिर रहेगा। हमारे उदाहरण में, इतना नहीं

  3. awaitकुछ चतुर कंपाइलर जादू के साथ कीवर्ड क्या करता है, यह है कि यह मूल रूप से "ठीक है, आप जानते हैं कि क्या है, मैं बस बटन पर क्लिक करके इवेंट हैंडलर से यहां लौटने जा रहा हूं। जब आप (जैसे हम, बात हम ' पुन: प्रतीक्षा) पूरा करने के लिए चारों ओर हो, मुझे बताएं क्योंकि मेरे पास अभी भी कुछ कोड निष्पादित करने के लिए बचा है "।

    वास्तव में यह SynchronizationContext क्लास को यह बताने देगा कि ऐसा किया गया है, जो कि वास्तविक सिंक्रनाइज़ेशन संदर्भ के आधार पर होता है, जो अभी चल रहा है, निष्पादन के लिए कतार में खड़ा होगा। Windows प्रपत्र प्रोग्राम में उपयोग किया जाने वाला संदर्भ वर्ग कतार का उपयोग करके इसे पंक्तिबद्ध करेगा कि संदेश लूप पंप कर रहा है।

  4. इसलिए यह संदेश लूप पर वापस लौटता है, जो अब संदेशों को पंप करना जारी रखने के लिए स्वतंत्र है, जैसे खिड़की को हिलाना, उसका आकार बदलना, या अन्य बटन पर क्लिक करना।

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

  5. 2 सेकंड बाद, जिस चीज़ का हम पूरा होने का इंतज़ार कर रहे हैं और अब जो होता है, वह यह है (ठीक है, सिंक्रोनाइज़ेशन का संदर्भ) एक संदेश को कतार में रखता है जिसे संदेश लूप देख रहा है, "अरे, मुझे कुछ और कोड मिला है" आप निष्पादित करने के लिए ", और यह कोड प्रतीक्षा के बाद का सभी कोड है ।
  6. जब संदेश लूप उस संदेश को प्राप्त हो जाता है, तो यह मूल रूप से उस विधि को "पुनः दर्ज" करेगा, जहां उसने इसे छोड़ दिया था, बस बाद में awaitऔर बाकी विधि को निष्पादित करना जारी रखें। ध्यान दें कि इस कोड को मैसेज लूप से फिर से कॉल किया जाता है ताकि यदि यह कोड async/awaitठीक से उपयोग किए बिना कुछ लंबा करने के लिए होता है , तो यह मैसेज लूप को फिर से ब्लॉक कर देगा

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


ठीक है, तो क्या होगा यदि GetSomethingAsync2 सेकंड में पूरा होने वाला एक धागा ऊपर घूम जाएगा? हां, तो जाहिर है कि नाटक में एक नया सूत्र है। यह धागा, तथापि, नहीं है क्योंकि इस विधि का async सत्ता का ऐसा इसलिए है क्योंकि इस विधि के प्रोग्रामर एक धागा एसिंक्रोनस कोड लागू करने के लिए चुना है। लगभग सभी अतुल्यकालिक I / O एक धागे का उपयोग नहीं करते हैं, वे विभिन्न चीजों का उपयोग करते हैं। async/await अपने आप से नए धागे नहीं उधेड़ते, लेकिन जाहिर है "जिन चीजों का हम इंतजार करते हैं" उन्हें धागे का उपयोग करके लागू किया जा सकता है।

.NET में बहुत सी चीजें हैं जो जरूरी नहीं कि एक धागा को अपने आप में स्पिन करें लेकिन अभी भी अतुल्यकालिक हैं:

  • वेब अनुरोध (और कई अन्य नेटवर्क से संबंधित चीजें जो समय लेती हैं)
  • अतुल्यकालिक फ़ाइल पढ़ना और लिखना
  • और कई और अधिक, एक अच्छा संकेत है, तो सवाल में वर्ग / इंटरफेस का नाम दिया है तरीकों है SomethingSomethingAsyncया BeginSomethingऔर EndSomethingऔर वहाँ एक है IAsyncResultशामिल।

आमतौर पर ये चीजें हुड के नीचे एक धागे का उपयोग नहीं करती हैं।


ठीक है, तो आप "व्यापक विषय सामान" में से कुछ चाहते हैं?

खैर, हमारे बटन क्लिक के बारे में रोज़लिन से पूछें :

रोजलिन की कोशिश करो

मैं यहाँ पूरी तरह से उत्पन्न वर्ग में लिंक नहीं करने जा रहा हूँ, लेकिन यह बहुत सुंदर सामान है।


11
तो यह मूल रूप से ओपी ने " कार्यों के निर्धारण और उनके बीच स्विच करके समानांतर निष्पादन का अनुकरण " के रूप में वर्णित किया है , है ना?
बरगी

4
@ बर्गी काफी नहीं। निष्पादन वास्तव में समानांतर है - अतुल्यकालिक I / O कार्य जारी है, और आगे बढ़ने के लिए कोई थ्रेड्स की आवश्यकता नहीं है (यह कुछ ऐसा है जो विंडोज के आसपास आने से बहुत पहले इस्तेमाल किया गया है - एमएस डॉस ने भी अतुल्यकालिक I / O का उपयोग किया, भले ही यह नहीं हुआ बहु सूत्रण है!)। बेशक, जिस तरह से आप इसका वर्णन करते हैं, वैसे ही इस्तेमाल किया await जा सकता है, लेकिन आमतौर पर ऐसा नहीं है। केवल कॉलबैक अनुसूचित हैं (थ्रेड पूल पर) - कॉलबैक और अनुरोध के बीच, किसी थ्रेड की आवश्यकता नहीं है।
लुआण

3
इसलिए मैं स्पष्ट रूप से इस बारे में बात करने से बचना चाहता था कि उस पद्धति ने क्या किया, क्योंकि यह प्रश्न विशेष रूप से async / प्रतीक्षा के बारे में था, जो अपने स्वयं के धागे नहीं बनाता है। जाहिर है, वे धागे के पूरा होने के लिए इंतजार करने के लिए इस्तेमाल किया जा सकता है ।
लास वी। कार्लसन

6
@ LasseV.Karlsen - मैं आपका शानदार जवाब दे रहा हूं, लेकिन मैं अभी भी एक विस्तार पर लटका हुआ हूं। मैं समझता हूं कि ईवेंट हैंडलर मौजूद है, जैसा कि चरण 4 में है, जो संदेश पंप को पंप जारी रखने की अनुमति देता है, लेकिन कब और कहां "चीज दो सेकंड लगती है" निष्पादित करना जारी रखता है यदि एक अलग थ्रेड पर नहीं? अगर यह UI थ्रेड पर निष्पादित होता है, तो यह वैसे भी निष्पादित करते समय संदेश पंप को अवरुद्ध कर देगा, क्योंकि इसे उसी थ्रेड पर कुछ समय निष्पादित करना होगा .. [जारी]] ...
rory.ap

3
मुझे संदेश पंप के साथ आपका स्पष्टीकरण पसंद है। जब सांत्वना एप्लिकेशन या वेब सर्वर में कोई संदेश पंप नहीं होता है तो आपकी व्याख्या कैसे भिन्न होती है? एक विधि की पश्चाताप कैसे प्राप्त किया जाता है?
Puchacz

95

मैं इसे अपने ब्लॉग पोस्ट में पूर्ण रूप से समझाता हूं कोई थ्रेड नहीं है

सारांश में, आधुनिक I / O सिस्टम DMA (डायरेक्ट मेमोरी एक्सेस) का भारी उपयोग करते हैं। नेटवर्क कार्ड, वीडियो कार्ड, एचडीडी कंट्रोलर, सीरियल / समानांतर पोर्ट, आदि पर विशेष समर्पित प्रोसेसर हैं। इन प्रोसेसरों में मेमोरी बस की सीधी पहुंच है, और सीपीयू के स्वतंत्र रूप से पूरी तरह से पढ़ने / लिखने को संभालते हैं। CPU को केवल डेटा युक्त मेमोरी में स्थान के डिवाइस को सूचित करने की आवश्यकता होती है, और तब तक अपना काम कर सकता है जब तक कि डिवाइस सीपीयू को एक बाधा नहीं उठाता है कि रीड / राइट पूरा हो गया है।

एक बार जब ऑपरेशन उड़ान में होता है, तो सीपीयू के लिए कोई काम नहीं होता है, और इस प्रकार कोई धागा नहीं होता है।


बस इसे स्पष्ट करने के लिए .. मुझे समझ में आ रहा है कि async-wait का उपयोग करते समय क्या होता है। थ्रेड निर्माण के बारे में - केवल उन डिवाइसों के लिए I / O अनुरोधों में कोई थ्रेड नहीं है, जैसे आपने कहा कि उनके स्वयं के प्रोसेसर हैं जो अनुरोध को स्वयं संभालता है? क्या हम मान सकते हैं कि सभी I / O अनुरोधों को ऐसे स्वतंत्र प्रोसेसर पर नियंत्रित किया जाता है जिसका अर्थ है टास्क का उपयोग करें। केवल सीपीयू बाउंड एक्शन पर?
योनातन नीर

@ योनातननीर: यह केवल अलग-अलग प्रोसेसर के बारे में नहीं है; किसी भी प्रकार की घटना संचालित प्रतिक्रिया स्वाभाविक रूप से अतुल्यकालिक है। Task.Runसीपीयू-बाउंड क्रियाओं के लिए सबसे उपयुक्त है , लेकिन इसमें कुछ अन्य उपयोग भी हैं।
स्टीफन क्लीयर

1
मैंने आपके लेख को पढ़ना समाप्त कर दिया है और अभी भी कुछ बुनियादी है जो मुझे समझ नहीं आ रहा है क्योंकि मैं वास्तव में ओएस के निचले स्तर के कार्यान्वयन से परिचित नहीं हूं। मुझे वह मिला जो आपने लिखा था: "लिखने का कार्य अब" उड़ान में है। "कितने धागे इसे संसाधित कर रहे हैं? कोई नहीं।" । तो यदि कोई थ्रेड नहीं हैं, तो एक थ्रेड पर नहीं होने पर ऑपरेशन स्वयं कैसे किया जाता है?
योनातन नीर

6
यह हजारों स्पष्टीकरण में लापता टुकड़ा है !!! वास्तव में I / O संचालन के साथ पृष्ठभूमि में कोई काम कर रहा है। यह एक धागा नहीं बल्कि एक अन्य समर्पित हार्डवेयर घटक है जो अपना काम कर रहा है!
the_dark_destructor

2
@PrabuWeerasinghe: संकलक एक संरचना बनाता है जो राज्य और स्थानीय चर रखता है। यदि किसी प्रतीक्षारत को पैदावार करने की आवश्यकता होती है (यानी, इसके कॉलर पर वापस लौटें), तो वह संरचना बॉक्सिंग की जाती है और ढेर पर रहती है।
स्टीफन क्लीरी

87

एक ही तरीका है कि एक कंप्यूटर एक समय में 1 से अधिक काम कर सकता है (1) वास्तव में एक समय में 1 से अधिक काम कर रहा है, (2) कार्यों को शेड्यूल करके और उनके बीच स्विच करके। तो अगर async-wait में से कोई भी नहीं करता है

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

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

किसी भी विधि को कॉल करने का मतलब है कि विधि के पूरा होने की प्रतीक्षा करना

वहाँ कुंजी है जो आप गायब हैं। एक विधि अपने काम के पूरा होने से पहले वापस आ सकती है । यह वहीं अतुल्यकालिक का सार है। एक विधि वापस आती है, यह एक कार्य देता है, जिसका अर्थ है "यह कार्य प्रगति पर है, मुझे बताएं कि यह पूरा होने पर क्या करना है"। विधि का कार्य नहीं किया गया है, भले ही वह वापस आ गया हो

प्रतीक्षा ऑपरेटर से पहले, आपको उस कोड को लिखना होगा जो इस तरह से निपटने के लिए स्विस पनीर के माध्यम से पिरोया गया स्पेगेटी जैसा दिखता है, जिसे पूरा करने के बाद हमारे पास काम करना है , लेकिन वापसी और पूरा होने के साथ desynchronized है । आवा आपको कोड लिखने की अनुमति देता है जो रिटर्न की तरह दिखता है और पूर्णता सिंक्रनाइज़ किया जाता है, उनके बिना वास्तव में सिंक्रनाइज़ किया जा रहा है।


अन्य आधुनिक उच्च-स्तरीय भाषाएं समान रूप से सहकारी व्यवहार का समर्थन करती हैं, (यानी फ़ंक्शन कुछ सामान, पैदावार करता है [संभवतः कॉल करने वाले को कुछ मूल्य / वस्तु भेज रहा है], जारी रहता है, जहां नियंत्रण वापस दिए जाने पर छोड़ दिया जाता है [संभवतः अतिरिक्त इनपुट के साथ आपूर्ति] )। पाइथन में जेनरेटर बहुत बड़े हैं, एक चीज के लिए।
JAB

2
@ जेएबी: बिल्कुल। जनरेटर को C # में "इट्रेटर ब्लॉक" कहा जाता है और yieldकीवर्ड का उपयोग करें । asyncC # में दोनों विधियाँ और पुनरावृत्तियाँ , coroutine का एक रूप हैं , जो एक फ़ंक्शन के लिए सामान्य शब्द है जो जानता है कि बाद में फिर से शुरू करने के लिए अपने वर्तमान ऑपरेशन को कैसे निलंबित किया जाए। कई भाषाओं में इन दिनों कॉरआउट या कोरटाइन-जैसे नियंत्रण प्रवाह हैं।
एरिक लिपर्ट

1
उपज की उपमा एक अच्छी है - यह एक प्रक्रिया के भीतर सहकारी मल्टीटास्किंग है। (और इस तरह सिस्टम-वाइड कोऑपरेटिव मल्टीटास्किंग के सिस्टम स्थिरता के मुद्दों से बचा जा सकता है)
user253751

3
मुझे लगता है कि IO के लिए उपयोग की जा रही "सीपीयू इंटरप्ट" की अवधारणा, बहुत सारे मॉडेम "प्रोग्रामर" के बारे में नहीं जानती है, इसलिए उन्हें लगता है कि प्रत्येक बिट IO के लिए प्रतीक्षा करने की आवश्यकता है।
इयान रिंगरोज

@ वेबक्रिएंट का एरिकलिपर्ट एसिंक्स मेथड वास्तव में अतिरिक्त धागा बनाता है, यहां देखें stackoverflow.com/questions/48366871/…
केविनबाई

28

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

अपने खुद के शोध के बाद, मुझे आखिरकार लापता टुकड़ा मिला select():। विशेष रूप से, आईओ बहुसंकेतन, विभिन्न नामों के तहत विभिन्न कर्नेल द्वारा कार्यान्वित: select(), poll(), epoll(), kqueue()। ये सिस्टम कॉल हैं , जबकि कार्यान्वयन विवरण भिन्न होते हैं, आपको देखने के लिए फ़ाइल विवरणकों के एक सेट में पास करने की अनुमति देता है । तब आप एक और कॉल कर सकते हैं जो तब तक ब्लॉक करता है जब तक कि देखे गए फ़ाइल डिस्क्रिप्टर में से कोई एक नहीं बदलता।

इस प्रकार, कोई IO ईवेंट (मुख्य ईवेंट लूप) के सेट पर प्रतीक्षा कर सकता है, जो पूरा होने वाले पहले ईवेंट को हैंडल करता है, और फिर ईवेंट लूप पर वापस नियंत्रण प्राप्त करता है। धोये और दोहराएं।

यह कैसे काम करता है? खैर, संक्षिप्त जवाब यह है कि यह कर्नेल और हार्डवेयर-स्तरीय जादू है। सीपीयू के अलावा एक कंप्यूटर में कई घटक होते हैं, और ये घटक समानांतर में काम कर सकते हैं। कर्नेल इन उपकरणों को नियंत्रित कर सकता है और कुछ संकेतों को प्राप्त करने के लिए सीधे उनके साथ संवाद कर सकता है।

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


25

awaitऔर asyncउपयोग कार्य नहीं धागे।

फ्रेम में टास्क ऑब्जेक्ट के रूप में कुछ काम निष्पादित करने के लिए तैयार थ्रेड्स का एक पूल है ; टास्क को पूल में जमा करने का अर्थ है एक स्वतंत्र का चयन करना, पहले से ही मौजूद 1 , कार्य कार्रवाई विधि को कॉल करने के लिए थ्रेड। टास्क
बनाना एक नई वस्तु बनाने की बात है, एक नया धागा बनाने की तुलना में कहीं तेज।

टास्क को देखते हुए कंटिन्यूएशन देना संभव है , थ्रेड खत्म होते ही इसे टास्क किया जाना एक नया टास्क ऑब्जेक्ट है।

चूंकि टास्क एस का async/awaitउपयोग करते हैं, वे एक नया धागा नहीं बनाते हैं


जबकि बाधा प्रोग्रामिंग तकनीक का उपयोग प्रत्येक आधुनिक ओएस में व्यापक रूप से किया जाता है, मुझे नहीं लगता कि वे यहां प्रासंगिक हैं।
आप एक ही CPU का उपयोग करके समानांतर (interleaved वास्तव में) में निष्पादित दो सीपीयू बंधुआ कार्य कर सकते हैं aysnc/await
यह केवल इस तथ्य के साथ नहीं समझाया जा सकता है कि ओएस आईओआरपी को कतारबद्ध करता है


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

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

method:
   instr1                  
   instr2
   await task1
   instr3
   instr4
   await task2
   instr5
   return value

यह कुछ इस तरह से रूपांतरित हो जाता है

int state = 0;

Task nextStep()
{
  switch (state)
  {
     case 0:
        instr1;
        instr2;
        state = 1;

        task1.addContinuation(nextStep());
        task1.start();

        return task1;

     case 1:
        instr3;
        instr4;
        state = 2;

        task2.addContinuation(nextStep());
        task2.start();

        return task2;

     case 2:
        instr5;
        state = 0;

        task3 = new Task();
        task3.setResult(value);
        task3.setCompleted();

        return task3;
   }
}

method:
   nextStep();

1 वास्तव में एक पूल की अपनी कार्य निर्माण नीति हो सकती है।


16

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

awaitस्वयं के उपयोग से यह आपके ऐप को जादुई रूप से उत्तरदायी नहीं बनाता है। यदि आप यूआई थ्रेड ब्लॉक्स से जिस विधि का इंतजार कर रहे हैं, अगर आप करते हैं, तो यह आपके यूआई को उसी तरह से अवरुद्ध कर देगा जैसे गैर-प्रतीक्षित संस्करण

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


3
यह पहली जगह में एक प्रतियोगिता नहीं है; यह एक सहयोग है!
एरिक लिपर्ट

16

यहां बताया गया है कि मैं यह सब कैसे देखता हूं, यह तकनीकी रूप से सटीक नहीं हो सकता है लेकिन यह मेरी मदद करता है, कम से कम :)।

मशीन पर मूल रूप से दो प्रकार के प्रसंस्करण (गणना) होते हैं:

  • सीपीयू पर होने वाली प्रोसेसिंग
  • प्रसंस्करण जो अन्य प्रोसेसर (GPU, नेटवर्क कार्ड, आदि) पर होता है, चलो उन्हें IO कहते हैं।

इसलिए, जब हम स्रोत कोड का एक टुकड़ा लिखते हैं, संकलन के बाद, हमारे द्वारा उपयोग की जाने वाली वस्तु के आधार पर (और यह बहुत महत्वपूर्ण है), प्रसंस्करण सीपीयू बाध्य होगा , या आईओ बाध्य होगा , और वास्तव में, यह एक संयोजन के लिए बाध्य हो सकता है दोनों।

कुछ उदाहरण:

  • यदि मैं FileStreamऑब्जेक्ट के लिखने की विधि (जो एक स्ट्रीम है) का उपयोग करता हूं , तो प्रोसेसिंग कहलाएगा, 1% सीपीयू बाउंड, और 99% आईओ बाउंड।
  • यदि मैं NetworkStreamऑब्जेक्ट के लिखने की विधि (जो एक स्ट्रीम है) का उपयोग करता हूं , तो प्रोसेसिंग कहलाएगा, 1% सीपीयू बाउंड, और 99% आईओ बाउंड।
  • यदि मैं Memorystreamऑब्जेक्ट के लिखने की विधि (जो एक स्ट्रीम है) का उपयोग करता हूं , तो प्रसंस्करण 100% सीपीयू बाध्य होगा।

इसलिए, जैसा कि आप देखते हैं, ऑब्जेक्ट-ओरिएंटेड प्रोग्रामर पॉइंट-ऑफ-व्यू से, हालांकि मैं हमेशा किसी Streamऑब्जेक्ट को एक्सेस कर रहा हूं , नीचे जो होता है वह ऑब्जेक्ट के अंतिम प्रकार पर बहुत अधिक निर्भर हो सकता है।

अब, चीजों को अनुकूलित करने के लिए, कभी-कभी कोड को समानांतर में चलाने में सक्षम होना उपयोगी होता है (ध्यान दें कि यदि संभव हो और / या आवश्यक हो तो मैं एसिंक्रोनस शब्द का उपयोग नहीं करता हूं)।

कुछ उदाहरण:

  • एक डेस्कटॉप ऐप में, मैं एक दस्तावेज़ प्रिंट करना चाहता हूं, लेकिन मैं इसके लिए इंतजार नहीं करना चाहता।
  • मेरा वेब सर्वर एक ही समय में कई क्लाइंट को सर्वर करता है, हर एक को अपने पेज समानांतर में मिल रहे हैं (क्रमबद्ध नहीं)।

Async / प्रतीक्षा से पहले, हमारे पास अनिवार्य रूप से इसके दो समाधान थे:

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

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

तो, असली जीत ज्यादातर IO बाउंड कार्यों पर होती है , कार्य जो CPU का उपयोग नहीं करते हैं, लेकिन async / प्रतीक्षा अभी भी केवल एक प्रोग्रामिंग मॉडल है, यह आपको यह निर्धारित करने में मदद नहीं करता है कि प्रसंस्करण आखिर में कैसे / कहाँ होगा।

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

इसलिए, मेरे उदाहरणों पर वापस आते हुए, मेमोरीस्ट्रीम पर async / प्रतीक्षा का उपयोग करते हुए मेरे राइट्स ऑपरेशन्स को करने से सीपीयू बंधी रहेगी (मुझे शायद इससे कोई फायदा नहीं होगा), हालाँकि मैं निश्चित रूप से फाइलों और नेटवर्क धाराओं के साथ इसका लाभ उठाऊंगा।


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

3

अन्य उत्तरों का सारांश:

Async / wait मुख्य रूप से IO बाउंड कार्यों के लिए बनाया गया है, क्योंकि उनका उपयोग करके, व्यक्ति कॉलिंग थ्रेड को ब्लॉक करने से बच सकता है। उनका मुख्य उपयोग यूआई थ्रेड्स के साथ होता है, जहां यह थ्रेड के लिए वांछित नहीं है IO बाउंड ऑपरेशन पर अवरुद्ध किया जाता है।

Async बनाता नहीं है यह स्वयं थ्रेड है। कॉलिंग विधि के थ्रेड का उपयोग async विधि को निष्पादित करने के लिए किया जाता है जब तक कि यह एक आवेग नहीं पाता है। एक ही थ्रेड तब async विधि कॉल से परे कॉलिंग विधि को निष्पादित करना जारी रखता है। कहा जाता है कि async विधि के भीतर, प्रतीक्षा से लौटने के बाद, थ्रेड पूल से थ्रेड पर निष्पादित किया जा सकता है - केवल एक अलग थ्रेड तस्वीर में आता है।


अच्छा सारांश, लेकिन मुझे लगता है कि पूरी तस्वीर देने के लिए इसे 2 और सवालों का जवाब देना चाहिए: 1. प्रतीक्षित कोड किस धागे पर निष्पादित किया जाता है? 2. उल्लिखित थ्रेड पूल को कौन नियंत्रित / कॉन्फ़िगर करता है - डेवलपर या रनटाइम वातावरण?
स्टोजके

1. इस मामले में, ज्यादातर प्रतीक्षित कोड एक IO बाउंड ऑपरेशन है जो CPU थ्रेड का उपयोग नहीं करेगा। यदि यह सीपीयू बाउंड ऑपरेशन के लिए प्रतीक्षा का उपयोग करने के लिए वांछित है, तो एक अलग टास्क को जन्म दिया जा सकता है। 2. थ्रेड पूल में धागा टास्क शेड्यूलर द्वारा प्रबंधित किया जाता है जो टीपीएल ढांचे का हिस्सा है।
वैभव कुमार

2

मैं इसे नीचे समझाने की कोशिश करता हूं। शायद किसी को यह मददगार लगे। मैं वहां था, उसने ऐसा किया, इसे प्रबलित किया, जब पास्कल में डॉस (अच्छे पुराने समय ...) में सरल गेम बनाए।

तो ... हर इवेंट में संचालित एप्लिकेशन में एक इवेंट लूप होता है जो कुछ इस तरह से होता है:

while (getMessage(out message)) // pseudo-code
{
   dispatchMessage(message); // pseudo-code
}

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

ईवेंट हैंडलर को तेजी से चलना चाहिए ताकि ईवेंट लूप अधिक घटनाओं के लिए सर्वेक्षण कर सके और UI उत्तरदायी बना रहे। यदि एक बटन क्लिक करने से इस तरह का महंगा ऑपरेशन शुरू हो जाता है तो क्या होता है?

void expensiveOperation()
{
    for (int i = 0; i < 1000; i++)
    {
        Thread.Sleep(10);
    }
}

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

इसलिए आप इसे बदलेंगे:

void expensiveOperation()
{
    doIteration(0);
}

void doIteration(int i)
{
    if (i >= 1000) return;
    Thread.Sleep(10); // Do a piece of work.
    postFunctionCallMessage(() => {doIteration(i + 1);}); // Pseudo code. 
}

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

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

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

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


0

दरअसल, async awaitचेन सीएलआर संकलक द्वारा निर्मित राज्य मशीन है।

async await हालाँकि, थ्रेड्स का उपयोग करता है जो TPL थ्रेड पूल का उपयोग कार्यों को निष्पादित करने के लिए कर रहे हैं।

आवेदन अवरुद्ध नहीं होने का कारण यह है कि राज्य मशीन निर्णय ले सकती है कि कौन सी सह-दिनचर्या को निष्पादित करना, दोहराना, जांचना और फिर से निर्णय लेना है।

आगे की पढाई:

क्या async और प्रतीक्षा उत्पन्न करता है?

Async इंतजार और उत्पन्न StateMachine

एसिंक्रोनस C # और F # (III।): यह कैसे काम करता है? - टॉमस पेट्रिसक

संपादित करें :

ठीक है। ऐसा लगता है कि मेरा विस्तार गलत है। हालाँकि मुझे यह बताना होगा कि राज्य मशीनें async awaitएस के लिए महत्वपूर्ण संपत्ति हैं । यहां तक ​​कि अगर आप अतुल्यकालिक I / O लेते हैं, तब भी आपको यह जांचने के लिए एक सहायक की आवश्यकता होती है कि क्या ऑपरेशन पूरा हो गया है इसलिए हमें अभी भी एक राज्य मशीन की आवश्यकता है और यह निर्धारित करें कि कौन सी दिनचर्या को एक साथ निष्पादित किया जा सकता है।


0

यह सीधे सवाल का जवाब नहीं दे रहा है, लेकिन मुझे लगता है कि यह एक इंटरसैटिंग एडिटोनल जानकारी है:

Async और इंतजार खुद से नए सूत्र नहीं बनाता है। लेकिन जहां आप async प्रतीक्षा का उपयोग करते हैं, उसके आधार पर, सिंक्रोनस भाग से पहले एक अलग थ्रेड पर चला जा सकता है, जो कि सिंक्रोउस भाग की तुलना में एक अलग थ्रेड पर चल सकता है (उदाहरण के लिए ASP.NET और ASP.NET कोर अलग व्यवहार करते हैं)।

UI- थ्रेड आधारित एप्लिकेशन (WinForms, WPF) में आप पहले और बाद में एक ही थ्रेड पर होंगे। लेकिन जब आप थ्रेड पूल थ्रेड पर async दूर का उपयोग करते हैं, तो प्रतीक्षा से पहले और बाद का धागा समान नहीं हो सकता है।

इस विषय पर एक शानदार वीडियो

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.