2010-10-29 24 views
58

मैं सी # की (और वीबी का) नई async सुविधाओं के बीच अलग नहीं दिख रहा है, और .NET 4.0 के Task Parallel Library। उदाहरण के लिए ले लो,, एरिक Lippert के कोड from here:सी # 5.0 की एसिंक-प्रतीक्षा सुविधा टीपीएल से अलग कैसे होती है?

async void ArchiveDocuments(List<Url> urls) { 
    Task archive = null; 
    for(int i = 0; i < urls.Count; ++i) { 
     var document = await FetchAsync(urls[i]); 
     if (archive != null) 
      await archive; 
     archive = ArchiveAsync(document); 
    } 
} 

ऐसा नहीं है कि await कीवर्ड दो विभिन्न प्रयोजनों की सेवा कर रहा है लगता है। पहली घटना (FetchAsync) मतलब करने के लिए, लगता है "यह मान विधि में बाद में प्रयोग किया जाता है और अपने कार्य को पूरा नहीं हुआ है, तो इंतजार जब तक यह जारी रखने से पहले पूरा करती है।" दूसरे उदाहरण (archive) मतलब करने के लिए, "इस कार्य को अभी तक पूरी नहीं हुई है, तो इंतजार अभी जब तक यह पूरा करता है।" लगता है यदि मैं गलत हूं, तो कृपया मुझे सही करें।

यह बस के रूप में आसानी से इस तरह लिखा नहीं जा सकता है?

void ArchiveDocuments(List<Url> urls) { 
    for(int i = 0; i < urls.Count; ++i) { 
     var document = FetchAsync(urls[i]);  // removed await 
     if (archive != null) 
      archive.Wait();      // changed to .Wait() 
     archive = ArchiveAsync(document.Result); // added .Result 
    } 
} 

मैं एक Task.Result जहां मूल्य वास्तव में जरूरत है, और दूसरा awaitTask.Wait() साथ, जहां इंतजार वास्तव में हो रहा है के साथ पहली await से बदल दिया है। कार्यक्षमता (1) पहले से ही लागू किया, और (2) बहुत करीब क्या वास्तव में कोड में हो रहा है के लिए अर्थ की दृष्टि से है।

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

मैं स्पष्ट रूप से यहाँ कुछ विशाल याद कर रहा हूँ; क्या कोई मुझे थोड़ा बेहतर समझने में मदद कर सकता है?

+0

भी देखें http://stackoverflow.com/questions/3513432/task-parallel-library-replacement-for-backgroundworker:

क्या इंतजार कोड के बिना 'का इंतजार है' इस उत्कृष्ट लिंक देखें कैसा दिखेगा देखने के लिए ? lq = 1 और http://stackoverflow.com/questions/12414601/async-await-vs-backgroundworker?lq=1 – nawfal

+0

इस लिंक को देखें जिसमें साफ उदाहरण हैं: http://richnewman.wordpress.com/2012/12/03/ट्यूटोरियल-एसिंक्रोनस-प्रोग्रामिंग-एसिंक-एंड-प्रतीक्षा-के-शुरुआती/ – nawfal

उत्तर

69

मुझे लगता है कि गलतफहमी यहां उठता है:

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

यह वास्तव में पूरी तरह से सही नहीं है। इन दोनों का एक ही अर्थ है।

अपने पहले मामले में:

var document = await FetchAsync(urls[i]); 

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

निष्पादन तब तक दूसरी प्रतीक्षा कॉल तक जारी रहेगा - उस समय, वही बात होगी - यदि Task<T> (संग्रह) पूरा नहीं हुआ है, तो कॉलिंग संदर्भ में निष्पादन जारी किया जाएगा - अन्यथा, संग्रह होगा सेट।

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

0

FetchAsync() करने के लिए कॉल अभी भी जब तक यह पूरा करता है को अवरुद्ध कर देगा (जब तक कि कॉल await भीतर एक बयान?) कुंजी है कि नियंत्रण फोन करने वाले (क्योंकि ArchiveDocuments विधि ही async के रूप में घोषित किया जाता है) को लौट गया। तो कॉलर खुशी से यूआई तर्क प्रसंस्करण जारी रख सकता है, घटनाओं का जवाब देता है, आदि

जब FetchAsync() पूरा हो जाता है, तो यह लूप को समाप्त करने के लिए कॉलर को बाधित करता है। यह ArchiveAsync() और ब्लॉक हिट करता है, लेकिन ArchiveAsync() शायद एक नया कार्य बनाता है, इसे शुरू करता है, और कार्य देता है। यह दूसरी लूप शुरू करने की अनुमति देता है, जबकि कार्य प्रसंस्करण कर रहा है।

दूसरा लूप FetchAsync() और ब्लॉक, कॉलर पर नियंत्रण लौटता है। जब FetchAsync() पूर्ण हो जाता है, तो यह फिर से प्रसंस्करण जारी रखने के लिए कॉलर को बाधित करता है। इसके बाद यह await archive हिट करता है, जो लूप 1 पूर्ण में बनाए गए Task तक कॉलर पर नियंत्रण देता है। एक बार यह कार्य पूरा हो जाने पर, कॉलर फिर से बाधित हो जाता है, और दूसरा पाश ArchiveAsync() पर कॉल करता है, जो एक प्रारंभिक कार्य प्राप्त करता है और लूप 3 शुरू करता है, विज्ञापन नोडम दोहराएं।

कुंजी लिफ्टर्स निष्पादित होने पर कॉलर को नियंत्रण वापस कर रहा है।

+1

ध्यान दें कि "भारी लिफ्टर्स" निष्पादित नहीं हो सकता है। वे बिल्कुल समानांतर नहीं हो सकते हैं। जब भी यह धागा निष्क्रिय हो जाता है, तो वे इस धागे पर निर्धारित कार्य की इकाइयां हो सकती हैं, उदाहरण के लिए। मैं समांतरता के साथ एसिंक्रोनि के एक * बहुत * को देख रहा हूं और मैं उस धारणा के लोगों को अव्यवस्थित करने के लिए उत्सुक हूं; asynchrony अक्सर * नहीं * अधिक कार्यकर्ता धागे उत्पादन के बारे में है। कुंजी वास्तव में कॉलर * पर नियंत्रण की वापसी * ​​* भारी लिफ्टर्स "के बाद * कुछ * किया गया है ताकि यह सुनिश्चित किया जा सके कि उनका कार्य भविष्य में कुछ समय पूरा हो जाए। –

+0

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

+0

@JamesKing, "_... को लॉन्च या शेड्यूल करने की आवश्यकता नहीं है": यह _eventually_ शेड्यूल करेगा या एस/टी लॉन्च करेगा, है ना? – bvgheluwe

24

वहाँ एक बड़ा अंतर है:

Wait() ब्लॉक, await ब्लॉक नहीं करता।यदि आप अपने जीयूआई थ्रेड पर ArchiveDocuments() के एसिंक संस्करण को चलाते हैं, तो जीयूआई उत्तरदायी रहेगा जबकि fetching और संग्रहण संचालन चल रहे हैं। आप Wait() साथ TPL संस्करण का उपयोग करते हैं, तो आपके जीयूआई अवरुद्ध हो जाएगा।

ध्यान दें कि asyncawait के बिंदु पर - किसी भी धागे को पेश किए बिना ऐसा करने का प्रबंधन करता है, नियंत्रण केवल संदेश लूप पर वापस आ जाता है। एक बार जब कार्य के लिए पूरा कर लिया है इंतजार कर रहे थे जा रहा है, विधि (निरंतरता) के शेष संदेश पाश पर कतारबद्ध है और जीयूआई धागा जहां यह दूर छोड़ दिया ArchiveDocuments चलते रहेंगे।

4

समस्या है कि यहाँ ArchiveDocuments के हस्ताक्षर भ्रामक है। इसमें void की स्पष्ट वापसी है लेकिन वास्तव में वापसी Task है। मेरे लिए शून्य सिंक्रोनस का तात्पर्य है क्योंकि इसे खत्म करने के लिए "प्रतीक्षा" करने का कोई तरीका नहीं है। समारोह के वैकल्पिक हस्ताक्षर पर विचार करें।

async Task ArchiveDocuments(List<Url> urls) { 
    ... 
} 

जब यह लिखा गया है तो मेरे लिए अंतर अधिक स्पष्ट है। ArchiveDocuments फ़ंक्शन वह नहीं है जो सिंक्रनाइज़ हो जाता है लेकिन बाद में समाप्त हो जाएगा।

+2

यह कोड बहुत अधिक समझ में आएगा यदि "संग्रह" सदस्य या स्थैतिक चर थे, और विधि के भीतर परिभाषित नहीं किया गया था ... ऐसा कहा जा रहा है कि, अतुल्यकालिक कार्यों को वापस करने के लिए पूरी तरह से मान्य और स्वीकार्य है, और "आग और भूल जाओ "मतलब, spec दस्तावेज के अनुसार। –

+0

क्या आप कह रहे हैं कि 'ArchiveDocuments()' पर कॉल 'कार्य कार्य = संग्रह दस्तावेज़ (सूची यूआरएल) जैसा दिखाई देगा; '? यह सही प्रतीत नहीं होता है ... और यदि मैं सही ढंग से समझता हूं, तो कॉलर वास्तव में बिल्कुल इंतजार नहीं कर रहा है। वास्तव में, यदि यह 'पुरालेख दस्तावेज़()' के नतीजे की परवाह करता है, तो पूरा परिदृश्य अलग हो जाता है और काम नहीं करता है। –

+1

@ रीड, मैं मानता हूं कि वे वास्तव में वैध और सही हैं। मुझे लगता है कि मैं इसे बहुत भ्रामक और थोड़ा अनुमान लगा सकता हूं क्योंकि शायद मैं भूलना नहीं चाहता;) – JaredPar

5

एक राज्य मशीन में नियंत्रण के प्रवाह प्रवाह को चालू करने की क्षमता इन नए खोजशब्दों को घुसपैठ कर देती है। इसके बारे में सोचें मूल्यों की बजाय नियंत्रण

चेक बाहर this Channel 9 video एंडर्स की नई सुविधा के बारे में बात।

23

एंडर्स ने इसे चैनल 9 लाइव साक्षात्कार में एक बहुत संक्षिप्त उत्तर में उबाल दिया। मैं अत्यधिक अनुशंसा करता हूं कि

नया असिनक और प्रतीक्षा करने वाले कीवर्ड आपको आपके अनुप्रयोगों में ऑर्केस्ट्रेट करने की अनुमति देते हैं। वे वास्तव में आपके आवेदन में कोई सहमति नहीं देते हैं।

TPL और अधिक विशेष टास्क एक ही रास्ता आप वास्तव में आपरेशन समवर्ती प्रदर्शन करने के लिए उपयोग कर सकते हैं। नया async और प्रतीक्षा कीवर्ड आपको को "समकालिक" या "रैखिक" फैशन में इन समवर्ती संचालनों को लिखने की अनुमति देता है।

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

+2

यह वास्तव में * कुछ भी नहीं कहता है, है ना? मुझे दोबारा दोहराएं: इस सवाल का जवाब कैसे दिया जाता है? –

+13

सवाल यह है कि "सी # 5.0 की एसिंक-प्रतीक्षा सुविधा टीपीएल से अलग कैसे होती है?" मेरा जवाब आईएमओ के लिए उपयुक्त है –

0

प्रतीक्षा कीवर्ड समवर्तीता का परिचय नहीं देता है। यह उपज कीवर्ड की तरह है, यह कंपाइलर को आपके कोड को राज्य मशीन द्वारा नियंत्रित लैम्ब्डा में पुन: स्थापित करने के लिए कहता है। http://blogs.msdn.com/b/windowsappdev/archive/2012/04/24/diving-deep-with-winrt-and-await.aspx

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