2013-03-19 25 views
56

के लिए आर्किटेक्चर यदि आप अपने आर्किटेक्चर में निचले स्तर पर async/await का उपयोग कर रहे हैं, तो क्या यह सभी तरह से एसिंक/प्रतीक्षा कॉल को "बबल अप" करना आवश्यक है, क्या यह मूल रूप से बना रहा है क्योंकि यह अक्षम है प्रत्येक परत के लिए एक नया धागा (प्रत्येक परत के लिए असीमित रूप से एसिंक्रोनस फ़ंक्शन को कॉल करना, या यह वास्तव में कोई फर्क नहीं पड़ता है और यह आपकी वरीयता पर निर्भर करता है?async/await

मैं ईएफ 6.0-अल्फा 3 का उपयोग कर रहा हूं ताकि मैं async विधियों को प्राप्त कर सकूं । एफई

मेरे भंडार इस तरह है:

public class EntityRepository<E> : IRepository<E> where E : class 
{ 
    public async virtual Task Save() 
    { 
     await context.SaveChangesAsync(); 
    } 
} 

अब मेरे व्यापार परत इस तरह के रूप में है:

public abstract class ApplicationBCBase<E> : IEntityBC<E> 
{ 
    public async virtual Task Save() 
    { 
     await repository.Save(); 
    } 
} 

और फिर निश्चित रूप से मेरी यूआई में मेरी विधि जब बुला पैटर्न का ही पालन करना होगा।

  1. आवश्यक
  2. प्रदर्शन
  3. वरीयता का मामला

यहां तक ​​कि अगर यह अलग परतों में नहीं किया जाता है/एक ही सवाल परियोजनाओं पर लागू होता है पर नकारात्मक:

इस है अगर मैं एक ही कक्षा में नेस्टेड विधियों को बुला रहा हूं:

private async Task<string> Dosomething1() 
    { 
     //other stuff 
     ... 
     return await Dosomething2(); 
    } 
    private async Task<string> Dosomething2() 
    { 
     //other stuff 
     ... 
     return await Dosomething3(); 
    } 
    private async Task<string> Dosomething3() 
    { 
     //other stuff 
     ... 
     return await Task.Run(() => ""); 
    } 

उत्तर

55

आप async उपयोग कर रहे हैं/अपने आर्किटेक्चर में निचले स्तर पर प्रतीक्षा करें, क्या एसिंक/प्रतीक्षा कॉल को सभी तरह से "बबल अप" करना आवश्यक है, यह अक्षम है क्योंकि आप मूल रूप से प्रत्येक परत के लिए एक नया धागा बना रहे हैं (प्रत्येक परत के लिए असीमित रूप से एसिंक्रोनस फ़ंक्शन को कॉल करना , या यह वास्तव में कोई फर्क नहीं पड़ता और केवल आपकी वरीयता पर निर्भर है?

यह प्रश्न गलतफहमी के कुछ क्षेत्रों को सुझाता है ।

सबसे पहले, प्रत्येक बार जब आप एसिंक्रोनस फ़ंक्शन को कॉल करते हैं तो एक नया धागा बनाएं।

दूसरा, आपको एसिंक विधि को घोषित करने की आवश्यकता नहीं है, सिर्फ इसलिए कि आप एसिंक्रोनस फ़ंक्शन को कॉल कर रहे हैं। आप कार्य है कि पहले से ही लौटाए जाने से खुश हैं, बस लौटने कि एक तरीका है जिसके नहीं async संशोधक है से:

public class EntityRepository<E> : IRepository<E> where E : class 
{ 
    public virtual Task Save() 
    { 
     return context.SaveChangesAsync(); 
    } 
} 

public abstract class ApplicationBCBase<E> : IEntityBC<E> 
{ 
    public virtual Task Save() 
    { 
     return repository.Save(); 
    } 
} 

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

किसी भी async विधि में आप किसी एकल await अभिव्यक्ति का इंतजार कर रहा एक Task या Task<T>, सही नहीं आगे की प्रक्रिया के साथ विधि के अंत में, बेहतर हो जाएगा async का उपयोग किए बिना लिखा जा रहा। तो यह:

public async Task<string> Foo() 
{ 
    var bar = new Bar(); 
    bar.Baz(); 
    return await bar.Quux(); 
} 

बेहतर लिखा है के रूप में:

public Task<string> Foo() 
{ 
    var bar = new Bar(); 
    bar.Baz(); 
    return bar.Quux(); 
} 

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

+0

ध्यान दें कि आप त्रुटि संदेश जोड़ने के लिए 'async' के रूप में वर्णित एक संदेश को भी चिह्नित कर सकते हैं। यदि आप कार्य का इंतजार कर रहे हैं तो आप प्रदान किए गए कार्य को जारी रखने के बजाय कॉल को 'कोशिश/पकड़' ब्लॉक में बस लपेट सकते हैं। – Servy

+0

@ सर्वी: हाँ, लेकिन उस स्थिति में यह कोई अतिरिक्त प्रसंस्करण प्रदान करने के विवरण को पूरा नहीं करेगा। यह आसान है कि एपीआई को बदलने के बिना * * कर सकता है, आपको दिमाग ... –

+0

तो, इसे समझने की कोशिश कर रहा है, मुझे वास्तव में केवल उसी एसिंक/प्रतीक्षा का उपयोग करने की आवश्यकता है यदि एक ही फ़ंक्शन में चलने वाली किसी चीज़ को एसिंक की वापसी की आवश्यकता होती है तरीका? अगर यह आग है और भूल गया है या आखिरी बयान है तो इंतजार की कोई ज़रूरत नहीं है? – valdetero

27

यह अक्षम है क्योंकि आप मूल रूप से प्रत्येक परत के लिए एक नया धागा बना रहे हैं (प्रत्येक परत के लिए असीमित रूप से एसिंक्रोनस फ़ंक्शन को कॉल करना, या यह वास्तव में कोई फर्क नहीं पड़ता है और यह आपकी वरीयता पर निर्भर है?

नहीं। असीमित तरीके आवश्यक रूप से नए धागे का उपयोग नहीं करते हैं। इस मामले में, अंतर्निहित एसिंक्रोनस विधि कॉल एक आईओ बाध्य विधि है, इसलिए वास्तव में कोई नया धागा नहीं बनाया जाना चाहिए।

यह है:

1. necessary 

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

2. negative on performance 

नहीं जैसा कि मैंने उल्लेख किया है, यह नए धागे नहीं बनाता है। कुछ ओवरहेड है, लेकिन इनमें से अधिकतर को कम किया जा सकता है (नीचे देखें)।

3. just a matter of preference 

यदि आप इस असीमित को रखना नहीं चाहते हैं। आपको स्टैक में चीजों को अतुल्यकालिक रखने के लिए ऐसा करने की आवश्यकता है।

अब, कुछ चीजें हैं जो आप perf को बेहतर बनाने के लिए कर सकते हैं। यहाँ।तुम सिर्फ एक अतुल्यकालिक विधि लपेटकर रहे हैं, तो आप भाषा सुविधाओं का उपयोग करने की जरूरत नहीं है - बस लौट Task:

public virtual Task Save() 
{ 
    return repository.Save(); 
} 

repository.Save() पद्धति पहले से ही वापस आने वाले Task - आप इसे बस का इंतजार करने की जरूरत नहीं इसे Task में वापस लपेटने के लिए। यह विधि को कुछ और अधिक कुशल बनाए रखेगा।

private async Task<string> Dosomething2() 
{ 
    //other stuff 
    ... 
    return await Dosomething3().ConfigureAwait(false); 
} 

यह नाटकीय रूप से भूमि के ऊपर प्रत्येक awaitमें शामिल करता है, तो आप की जरूरत नहीं है कम कर देता है:

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

अंत में, मैं अपने उदाहरणों में से एक के खिलाफ चेतावनी देते हैं:

private async Task<string> Dosomething3() 
{ 
    //other stuff 
    ... 
    // Potentially a bad idea! 
    return await Task.Run(() => ""); 
} 

आप एक async विधि है जो आंतरिक रूप से, कुछ है कि अतुल्यकालिक ही नहीं है चारों ओर "asynchrony बनाने" के लिए Task.Run उपयोग कर रहा है कर रहे हैं, आप एक एसिंक विधि में सिंक्रोनस कोड को प्रभावी ढंग से लपेट रहे हैं। यह थ्रेडपूल थ्रेड का उपयोग करेगा, लेकिन यह तथ्य "छुपा" सकता है कि यह ऐसा कर रहा है, प्रभावी ढंग से एपीआई भ्रामक बना रहा है। अपने उच्चतम स्तर की कॉल के लिए Task.Run पर कॉल छोड़ना अक्सर बेहतर होता है, और अंतर्निहित विधियां सिंक्रोनस रहती हैं जब तक वे वास्तव में एसिंक्रोनस आईओ या Task.Run के अलावा अनलोडिंग के कुछ साधनों का लाभ उठाने में सक्षम न हों। (यह हमेशा सच नहीं है, लेकिन "async" Task.Run के माध्यम से तुल्यकालिक कोड पर लिपटे कोड, तो async के माध्यम से लौटे/इंतजार है अक्सर एक त्रुटिपूर्ण डिजाइन का एक संकेत है।)

+0

अंतिम विधि में 'टास्क.रुन()' नमूना उद्देश्यों के लिए सिर्फ एक संक्षिप्त उदाहरण था। यह इस मामले में 'कार्य' या 'कार्य ' वापस करने वाली कोई भी विधि होगी/हो सकती है। मैं सिर्फ तरीकों के घोंसले को दिखाना चाहता था। – valdetero

+2

@ valdetero हां, लेकिन विशिष्ट उदाहरण एसिंक/प्रतीक्षा के साथ एक विरोधी पैटर्न का प्रभावी ढंग से उपयोग कर रहा है जिसे लोगों को पता नहीं है। मैंने सोचा कि मैं इसे इंगित करता हूं, हालांकि मैंने यह भी बताया कि यह जरूरी नहीं है कि सावधानीपूर्वक उपयोग करने के लिए कुछ भी गलत है। –

+0

वापसी कार्य। कॉम्प्लेटेड टास्क; –