यदि मेरे इंटरफ़ेस में टास्क वापस करना चाहिए तो नो-ऑपरेशन कार्यान्वयन का सबसे अच्छा तरीका क्या है?


435

नीचे दिए गए कोड में, इंटरफ़ेस के कारण, कक्षा LazyBarको यह कार्य विधि से वापस करना चाहिए (और तर्क के लिए बदला नहीं जा सकता)। यदि LazyBarक्रियान्वयन असामान्य है, तो यह जल्दी और तुल्यकालिक रूप से चलने के लिए होता है - विधि से नो-ऑपरेशन कार्य को वापस करने का सबसे अच्छा तरीका क्या है?

मैं Task.Delay(0)नीचे के साथ चला गया हूं , हालांकि मैं यह जानना चाहूंगा कि क्या इसका कोई प्रदर्शन साइड-इफेक्ट है अगर फ़ंक्शन को बहुत कुछ कहा जाता है (तर्क के लिए, सैकड़ों बार एक सेकंड में कहें):

  • क्या यह संश्लिष्ट चीनी अन-पवन कुछ बड़ा करती है?
  • क्या यह मेरे आवेदन के थ्रेड पूल को रोकना शुरू कर देता है?
  • संकलक क्लीवर Delay(0)अलग से निपटने के लिए पर्याप्त है ?
  • return Task.Run(() => { });कोई अलग होगा ?

क्या कोई बेहतर तरीका है?

using System.Threading.Tasks;

namespace MyAsyncTest
{
    internal interface IFooFace
    {
        Task WillBeLongRunningAsyncInTheMajorityOfImplementations();
    }

    /// <summary>
    /// An implementation, that unlike most cases, will not have a long-running
    /// operation in 'WillBeLongRunningAsyncInTheMajorityOfImplementations'
    /// </summary>
    internal class LazyBar : IFooFace
    {
        #region IFooFace Members

        public Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
        {
            // First, do something really quick
            var x = 1;

            // Can't return 'null' here! Does 'Task.Delay(0)' have any performance considerations?
            // Is it a real no-op, or if I call this a lot, will it adversely affect the
            // underlying thread-pool? Better way?
            return Task.Delay(0);

            // Any different?
            // return Task.Run(() => { });

            // If my task returned something, I would do:
            // return Task.FromResult<int>(12345);
        }

        #endregion
    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            Test();
        }

        private static async void Test()
        {
            IFooFace foo = FactoryCreate();
            await foo.WillBeLongRunningAsyncInTheMajorityOfImplementations();
            return;
        }

        private static IFooFace FactoryCreate()
        {
            return new LazyBar();
        }
    }
}

2
संबंधित प्रश्न: stackoverflow.com/questions/4245968/create-a-completed-task
CodesInChaos

8
व्यक्तिगत रूप से मैं साथ जाऊंगा Task.FromResult<object>(null)
कोडइन्चोस 18

जवाबों:


625

उपयोग करने Task.FromResult(0)या Task.FromResult<object>(null)एक से अधिक Taskअभिव्यक्ति के साथ बनाने की तुलना में कम ओवरहेड को उकसाएगा । Taskपूर्व-निर्धारित परिणाम के साथ बनाते समय, कोई शेड्यूलिंग ओवरहेड शामिल नहीं होता है।


आज, मैं इसे पूरा करने के लिए Task.CompletedTask का उपयोग करने की सलाह दूंगा


5
और यदि आप github.com/StephenCleary/AsyncEx का उपयोग करते हैं, तो वे कई अन्य उपयोगी कार्यों (0 इंट, सच / गलत, डिफ़ॉल्ट <टी> ()
क्वेंटिन-

5
return default(YourReturnType);
महापुरूष

8
@Legends जो सीधे टास्क बनाने के लिए काम नहीं करता है
रीड कोपसी

18
im यकीन नहीं है, लेकिन Task.CompletedTaskचाल हो सकती है! (लेकिन .net 4.6 की आवश्यकता है)
पीटर

1
प्रश्न: यह कैसे होता है Task.FromResult<TResult>, जो रिटर्न करता है Task<TResult>, रिटर्न प्रकार Task(कोई प्रकार के परम) को संतुष्ट करता है ?
rory.ap

187

का उपयोग करने के बारे में रीड कोपसी के जवाब में जोड़ने के लिए Task.FromResult, आप प्रदर्शन को और अधिक बेहतर कर सकते हैं यदि आप पहले से ही पूरा किए गए कार्य को कैश करते हैं क्योंकि पूरा किए गए कार्यों के सभी उदाहरण समान हैं:

public static class TaskExtensions
{
    public static readonly Task CompletedTask = Task.FromResult(false);
}

साथ TaskExtensions.CompletedTaskआप पूरे अनुप्रयोग डोमेन भर एक ही उदाहरण का उपयोग कर सकते हैं।


.Net फ्रेमवर्क (v4.6) का नवीनतम संस्करणTask.CompletedTask स्थैतिक संपत्ति के साथ ही जोड़ता है

Task completedTask = Task.CompletedTask;

क्या मुझे इसे वापस करने या उस पर प्रतीक्षा करने की आवश्यकता है?
पिक्सर 16

@ पीक्सर तुम्हारा क्या मतलब है? आप दोनों कर सकते हैं, लेकिन इसकी प्रतीक्षा करना समकालिक रूप से जारी रहेगा।
i3arnon

क्षमा करें, मैं संदर्भ का उल्लेख किया था :) मैं इसे अभी देखना रूप में, हम कर सकते हैं public Task WillBeLongRunningAsyncInTheMajorityOfImplementations()और साथ ही public async Task WillBeLongRunningAsyncInTheMajorityOfImplementations()। तो, हम return CompletedTask;या तो कर सकते हैं await CompletedTask;। अधिक अधिमान्य क्या है (शायद अधिक कुशल या अधिक सर्वांगीण)?
पिक्सार

3
@Pixar मैं स्पष्ट नहीं है। मेरा मतलब था "'नो-एसिंक्स' अधिक कुशल होगा"। एक विधि को async बनाना कंपाइलर को राज्य-मशीन में बदलने का निर्देश देता है। हर बार कॉल करने पर यह एक नया कार्य भी करेगा। पहले से ही पूर्ण किए गए कार्य को वापस करना स्पष्ट और अधिक प्रदर्शनकारी होगा।
i3arnon

3
@ इसके अलावा यह आवंटन कम कर देता है (और इसके साथ जीसी समय)। नई मेमोरी को आवंटित करने और टास्क इंस्टेंस के निर्माण के बजाय हर बार जब आपको एक पूर्ण टास्क की आवश्यकता होती है, तो आप केवल एक बार ऐसा करते हैं।
i3arnon

38

Task.Delay(0)जैसा कि स्वीकृत उत्तर में एक अच्छा दृष्टिकोण था, क्योंकि यह एक पूर्ण की कैश्ड प्रति है Task

4.6 के रूप में अब वहाँ है Task.CompletedTaskजो अपने उद्देश्य में अधिक स्पष्ट है, लेकिन न केवल Task.Delay(0)अभी भी एक कैश्ड उदाहरण लौटाता है, यह उतना ही कैश्ड उदाहरण देता है Task.CompletedTask

न की कैश्ड प्रकृति नियत रहने की गारंटी है, लेकिन कार्यान्वयन पर निर्भर अनुकूलन है कि केवल कार्यान्वयन पर निर्भर अनुकूलन के रूप में कर रहे हैं के रूप में (है कि, वे अभी भी सही ढंग से करता है, तो कार्यान्वयन कुछ है कि अभी भी मान्य था करने के लिए बदल काम करेंगे) के उपयोग Task.Delay(0)था स्वीकृत उत्तर से बेहतर है।


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

2
मुझे पता नहीं क्यों, लेकिन Task.CompletedTaskपीसीएल परियोजना में इस्तेमाल नहीं किया जा सकता है, भले ही मैंने .net संस्करण को 4.6 (प्रोफाइल 7) पर सेट किया हो, बस वीएस2017 में परीक्षण किया गया।
फेलिक्स

@ मुझे लगता है कि यह पीसीएल एपीआई की सतह का हिस्सा नहीं होना चाहिए, हालांकि इस समय केवल एक चीज जो पीसीएल का समर्थन करती है वह भी 4.5 का समर्थन करती है इसलिए मैं पहले से ही अपने Task.CompletedTask => Task.Delay(0);समर्थन का उपयोग करने के लिए तैयार हूं , इसलिए मैं डॉन मेरे सिर के ऊपर से सुनिश्चित करने के लिए पता नहीं है।
जॉन हन्ना

17

हाल ही में इसका सामना करना पड़ा और विधि के शून्य होने के बारे में चेतावनी / त्रुटियां मिलती रहीं।

हम संकलक को प्लाक करने के व्यवसाय में हैं और यह इसे साफ करता है:

    public async Task MyVoidAsyncMethod()
    {
        await Task.CompletedTask;
    }

यह अब तक की सभी सलाहों को एक साथ लाता है। जब तक आप वास्तव में विधि में कुछ नहीं कर रहे हैं तब तक कोई वापसी विवरण आवश्यक नहीं है।


17
जो कि पूरी तरह से गलत है। आपको कंपाइलर त्रुटि हो रही है क्योंकि विधि परिभाषा में async है, इसलिए कंपाइलर एक प्रतीक्षा की अपेक्षा करता है। "सही" उपयोग सार्वजनिक कार्य होगा MyVoidAsyncMethog () {वापसी Task.CompletedTask;}
कीथ

3
यकीन नहीं क्यों यह नीचे वोट दिया गया था क्योंकि यह सबसे साफ जवाब लगता है
वेबवेक

3
कीथ की टिप्पणी के कारण।
noelicus

4
वह पूरी तरह से गलत नहीं है, उसने सिर्फ एसिंक्स कीवर्ड हटा दिया है। मेरा दृष्टिकोण अधिक मुहावरेदार है। उसकी न्यूनतावादी है। अगर थोड़ा असभ्य नहीं है।
अलेक्जेंडर ट्रूजि

1
इसका कोई मतलब नहीं है, यहां कीथ के साथ पूरी तरह से सहमत हूं, मुझे वास्तव में सभी उत्थान नहीं मिलते हैं। आप ऐसा कोड क्यों जोड़ेंगे जो आवश्यक नहीं है? public Task MyVoidAsyncMethod() {}पूरी तरह से उपरोक्त विधि के समान है। यदि इस तरह से इसका उपयोग करने के लिए कोई usecase है, तो कृपया अतिरिक्त कोड जोड़ें।
निक एन


9

जब आपको निर्दिष्ट प्रकार वापस करना होगा:

Task.FromResult<MyClass>(null);

3

मैं Task completedTask = Task.CompletedTask;.Net 4.6 के समाधान को पसंद करता हूं , लेकिन एक और तरीका है कि एस्किंस और रिटर्न शून्य की विधि को चिह्नित करें:

    public async Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
    {
    }

आपको एक चेतावनी मिलेगी (CS1998 - Async फ़ंक्शन विदाउट एक्सप्रेशन), लेकिन इस संदर्भ में इसे अनदेखा करना सुरक्षित है।


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

0

यदि आप जेनरिक का उपयोग कर रहे हैं, तो सभी उत्तर हमें संकलित त्रुटि देंगे। आप उपयोग कर सकते हैं return default(T);। आगे की व्याख्या करने के लिए नीचे नमूना।

public async Task<T> GetItemAsync<T>(string id)
            {
                try
                {
                    var response = await this._container.ReadItemAsync<T>(id, new PartitionKey(id));
                    return response.Resource;
                }
                catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
                {

                    return default(T);
                }

            }

क्यों होता है पतन?
कार्तिकेयन वीके

सवाल async विधियों के बारे में नहीं था :)
फ्रोड निल्सन

0
return await Task.FromResult(new MyClass());

3
हालांकि यह कोड प्रश्न को हल कर सकता है, जिसमें यह भी बताया गया है कि यह समस्या कैसे और क्यों हल करती है, इससे वास्तव में आपके पोस्ट की गुणवत्ता को बेहतर बनाने में मदद मिलेगी, और संभवत: अधिक वोट मिले। याद रखें कि आप भविष्य में पाठकों के लिए प्रश्न का उत्तर दे रहे हैं, न कि केवल उस व्यक्ति से जो अब पूछ रहा है। स्पष्टीकरण जोड़ने के लिए कृपया अपना उत्तर संपादित करें और संकेत दें कि क्या सीमाएँ और मान्यताएँ लागू होती हैं।
डेविड बक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.