2013-02-15 32 views
37

एमएसडीएन दस्तावेज यह बताता है कि async और await आईओ-बाध्य कार्यों के लिए उपयुक्त हैं जबकि Task.Run CPU-bound कार्यों के लिए उपयोग किया जाना चाहिए।क्या आईएसओ और सीपीयू दोनों विधियों के लिए async/प्रतीक्षा उपयुक्त है?

मैं ऐसे एप्लिकेशन पर काम कर रहा हूं जो एचटीएमएल दस्तावेजों को पुनः प्राप्त करने के लिए HTTP अनुरोध करता है, जो इसे तब पार्स करता है। मैं एक विधि है कि इस तरह दिखता है:

public async Task<HtmlDocument> LoadPage(Uri address) 
{ 
    using (var httpResponse = await new HttpClient().GetAsync(address)) //IO-bound 
    using (var responseContent = httpResponse.Content) 
    using (var contentStream = await responseContent.ReadAsStreamAsync()) 
     return await Task.Run(() => LoadHtmlDocument(contentStream)); //CPU-bound 
} 

async और await के इस अच्छा और उपयुक्त उपयोग है, या मैं अधिक उपयोग करने से यह कर रहा हूँ?

उत्तर

31

दो अच्छा पहले से ही जवाब नहीं है, लेकिन जोड़ने के लिए मेरी 0.02 ...

आप के बारे में के लिए दोनों आई/ओ बाध्य और CPU- बात कर रहे हैं लेने वाली अतुल्यकालिक संचालन, async/await काम करता है उत्कृष्ट बाध्य।

मुझे लगता है कि MSDN डॉक्स जिस स्थिति में आप के लिए मैं/हे बाध्य और TaskCompletionSource (या समान) उपयोग करना चाहते हैं, अतुल्यकालिक संचालन के उत्पादन की दिशा में एक मामूली तिरछा है Task.Run (या समान) सीपीयू बाध्य के लिए ।एक बार जब आपने शुरुआती Task रैपर बनाया है, तो यह async और await द्वारा उपभोग किया गया है।

आपके विशेष उदाहरण के लिए, यह वास्तव में नीचे आता है कि LoadHtmlDocument कितना समय लगेगा। यदि आप Task.Run हटाते हैं, तो आप इसे उसी संदर्भ में निष्पादित करेंगे जो LoadPage (संभवतः यूआई थ्रेड पर) को कॉल करता है। विंडोज 8 दिशानिर्देश बताते हैं कि 50 एमएमएस से अधिक लेने वाले किसी भी ऑपरेशन को async बनाया जाना चाहिए ... ध्यान रखें कि आपकी डेवलपर मशीन पर 50 एमएमएस ग्राहक की मशीन पर अधिक हो सकता है ...

तो यदि आप गारंटी दे सकते हैं कि LoadHtmlDocument कम से कम 50ms के लिए चलाने के लिए, तुम सिर्फ यह सीधे निष्पादित कर सकते हैं:

public async Task<HtmlDocument> LoadPage(Uri address) 
{ 
    using (var httpResponse = await new HttpClient().GetAsync(address)) //IO-bound 
    using (var responseContent = httpResponse.Content) 
    using (var contentStream = await responseContent.ReadAsStreamAsync()) //IO-bound 
    return LoadHtmlDocument(contentStream); //CPU-bound 
} 

हालांकि, मैं ConfigureAwait सिफारिश करेंगे @svick रूप में उल्लेख किया:

public async Task<HtmlDocument> LoadPage(Uri address) 
{ 
    using (var httpResponse = await new HttpClient().GetAsync(address) 
     .ConfigureAwait(continueOnCapturedContext: false)) //IO-bound 
    using (var responseContent = httpResponse.Content) 
    using (var contentStream = await responseContent.ReadAsStreamAsync() 
     .ConfigureAwait(continueOnCapturedContext: false)) //IO-bound 
    return LoadHtmlDocument(contentStream); //CPU-bound 
} 
ConfigureAwait साथ

, अगर टी वह HTTP अनुरोध तत्काल (सिंक्रनाइज़) पूरा नहीं होता है, तो यह पर एक स्पष्ट कॉल के बिना थ्रेड पूल थ्रेड पर निष्पादित करने के लिए LoadHtmlDocument का कारण होगा (इस मामले में)।

यदि आप इस स्तर पर async प्रदर्शन में रुचि रखते हैं, तो आपको इस विषय पर स्टीफन टब के video और MSDN article पर ध्यान देना चाहिए। उसके पास बहुत उपयोगी जानकारी है।

12

यह await के लिए उपयुक्त है जो असीमित है (यानी Task द्वारा दर्शाया गया है)।

मुख्य बिंदु यह है कि जब भी संभव हो, आईओ संचालन के लिए, आप एक अवरुद्ध सिंक्रोनस विधि पर Task.Run का उपयोग करने के बजाय, एक बहुत ही महत्वपूर्ण, असीमित पर उपयोग की गई विधि का उपयोग करना चाहते हैं। यदि आप आईओ प्रदर्शन करते समय थ्रेड (यहां तक ​​कि थ्रेड पूल थ्रेड) को अवरुद्ध कर रहे हैं, तो आप await मॉडल की वास्तविक शक्ति का लाभ नहीं उठा रहे हैं।

एक बार जब आपने Task बनाया है जो आपके ऑपरेशन का प्रतिनिधित्व करता है तो आप अब सीपीयू या आईओ बाध्य होने पर परवाह नहीं करते हैं। कॉलर को यह केवल कुछ एसिंक ऑपरेशन है जिसे await -ed होना चाहिए।

17

कई बातों पर विचार करना हैं:

  • एक जीयूआई आवेदन में, आप यूआई धागे पर अमल करने के लिए यथासंभव कम कोड चाहते हैं। उस स्थिति में, Task.Run() का उपयोग कर CPU थ्रेड ऑपरेशन को किसी अन्य थ्रेड पर ऑफ़लोड करना शायद एक अच्छा विचार है। यद्यपि वे आपके कोड के उपयोगकर्ता खुद को कर सकते हैं, अगर वे चाहते हैं।
  • एएसपी.NET एप्लिकेशन की तरह कुछ में, कोई UI थ्रेड नहीं है और आप जिस पर ध्यान रखते हैं वह प्रदर्शन है। उस स्थिति में, सीधे कोड चलाने के बजाय Task.Run() का उपयोग करने में कुछ ओवरहेड है, लेकिन ऑपरेशन वास्तव में कुछ समय लेता है तो यह महत्वपूर्ण नहीं होना चाहिए। (इसके अलावा, सिंक्रनाइज़ेशन संदर्भ पर लौटने में कुछ ओवरहेड है, जो कि आपको अपने पुस्तकालय कोड में await एस के लिए ConfigureAwait(false) का उपयोग करने का एक और कारण है।)
  • यदि आपकी विधि async है (जिसे बीटीडब्लू में भी प्रतिबिंबित किया जाना चाहिए विधि का नाम, न केवल इसके रिटर्न प्रकार), लोग उम्मीद करेंगे कि यह सिंक्रनाइज़ेशन संदर्भ थ्रेड को अवरुद्ध नहीं करेगा, यहां तक ​​कि सीपीयू-बाध्य काम के लिए भी।

भारोत्तोलन, मुझे लगता है कि await Task.Run() का उपयोग करना सही विकल्प है। इसमें कुछ ओवरहेड है, लेकिन कुछ फायदे भी हैं, जो महत्वपूर्ण हो सकते हैं।

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

  • कोई संबंधित समस्या नहीं^_^