मुझे सिंक वाले के बजाय async WebAPI संचालन क्यों बनाना चाहिए?


109

मेरे द्वारा बनाए गए वेब एपीआई में मेरा निम्नलिखित ऑपरेशन है:

// GET api/<controller>
[HttpGet]
[Route("pharmacies/{pharmacyId}/page/{page}/{filter?}")]
public CartTotalsDTO GetProductsWithHistory(Guid pharmacyId, int page, string filter = null ,[FromUri] bool refresh = false)
{
    return delegateHelper.GetProductsWithHistory(CustomerContext.Current.GetContactById(pharmacyId), refresh);
}

इस वेबसर्वर को कॉल Jquery Ajax के माध्यम से किया जाता है।

$.ajax({
      url: "/api/products/pharmacies/<%# Farmacia.PrimaryKeyId.Value.ToString() %>/page/" + vm.currentPage() + "/" + filter,
      type: "GET",
      dataType: "json",
      success: function (result) {
          vm.items([]);
          var data = result.Products;
          vm.totalUnits(result.TotalUnits);
      }          
  });

मैंने कुछ डेवलपर्स को देखा है जो पिछले ऑपरेशन को इस तरह से लागू करते हैं:

// GET api/<controller>
[HttpGet]
[Route("pharmacies/{pharmacyId}/page/{page}/{filter?}")]
public async Task<CartTotalsDTO> GetProductsWithHistory(Guid pharmacyId, int page, string filter = null ,[FromUri] bool refresh = false)
{
    return await Task.Factory.StartNew(() => delegateHelper.GetProductsWithHistory(CustomerContext.Current.GetContactById(pharmacyId), refresh));
}

हालांकि, कहना होगा कि GetProductsWithHistory () काफी लंबा ऑपरेशन है। मेरी समस्या और संदर्भ को देखते हुए, वेबपीआई ऑपरेशन अतुल्यकालिक लाभ कैसे प्रदान करेगा?


1
ग्राहक पक्ष AJAX का उपयोग करता है, जो पहले से ही अतुल्यकालिक है। आपको एक के रूप में भी लिखे जाने के लिए सेवा की आवश्यकता नहीं है async Task<T>। याद रखें, AJAX को TPL के अस्तित्व में आने से पहले लागू किया गया था :)
डोमिनिक ज़ुकीविज़

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

3
आपने क्या डेवलपर्स देखे हैं? यदि कोई ब्लॉग पोस्ट या लेख है जो उस तकनीक की सिफारिश करता है, तो कृपया एक लिंक पोस्ट करें।
स्टीफन क्लीयर

3
यदि आपकी प्रक्रिया ऊपर से (वेब ​​अनुप्रयोग स्वयं और आपके नियंत्रकों सहित) आपकी प्रक्रिया के बाहर होने वाली किसी भी तरह की प्रतीक्षा योग्य गतिविधियों (टाइमर की देरी, फ़ाइल I / O, DB का उपयोग सहित) से नीचे है, तो आपको केवल async का पूर्ण लाभ मिलता है। और वेब अनुरोध इसे बनाता है)। इस स्थिति में, आपके प्रतिनिधि सहायक को GetProductsWithHistoryAsync()वापस लौटने की आवश्यकता है Task<CartTotalsDTO>। अगर आप कॉल करने के लिए माइग्रेट करने का इरादा रखते हैं, तो यह आपके लिए async लिखने का एक लाभ हो सकता है, यह भी async होना चाहिए; जैसे ही आप बाकी हिस्सों को माइग्रेट करते हैं तो आपको एसिंक्स पार्ट्स से लाभ मिलना शुरू हो जाता है।
कीथ रॉबर्टसन

1
यदि आप जो प्रक्रिया कर रहे हैं वह बंद हो रही है और डेटाबेस को मार रहा है तो आपका वेब थ्रेड वापस आने और उस थ्रेड को पकड़े रहने की प्रतीक्षा कर रहा है। यदि आपने अपनी अधिकतम थ्रेड काउंट को मारा है और एक अन्य अनुरोध आता है, तो उसे इंतजार करना होगा। ऐसा क्यों करते हैं? इसके बजाय आप अपने नियंत्रक से उस धागे को मुक्त करना चाहते हैं, इसलिए एक अन्य अनुरोध इसका उपयोग कर सकता है और डेटाबेस से आपका मूल अनुरोध वापस आने पर केवल एक और वेब धागा ले सकता है। msdn.microsoft.com/en-us/magazine/dn802603.aspx
user441521

जवाबों:


98

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

"सिंक पर एसिंक्स" की मेरी चर्चा में, मैंने दृढ़ता से सुझाव दिया कि यदि आपके पास एक एपीआई है जिसे आंतरिक रूप से सिंक्रोनाइज़ किया जाता है, तो आपको एक एसिंक्रोनस समकक्ष को उजागर नहीं करना चाहिए जो बस सिंक्रोनस विधि को लपेटता है Task.Run

से चाहिए मैं अतुल्यकालिक तरीकों के लिए तुल्यकालिक रैपर का पर्दाफाश?

हालाँकि जब WebAPI कॉल करता है asyncतो एक वास्तविक एसिंक्रोनस ऑपरेशन होता है (आमतौर पर I / O) एक थ्रेड को ब्लॉक करने के बजाय जो कि बैठता है और एक परिणाम के लिए प्रतीक्षा करता है थ्रेड वापस थ्रेड पूल में जाता है और इसलिए कुछ अन्य ऑपरेशन करने में सक्षम होता है। इन सब से अधिक इसका मतलब है कि आपका आवेदन कम संसाधनों के साथ अधिक कर सकता है और इससे स्केलेबिलिटी में सुधार होता है।


3
@ एफारुक सभी धागे श्रमिक सूत्र हैं। एक थ्रेडपूल थ्रेड जारी करना और दूसरा ब्लॉक करना व्यर्थ है।
i3arnon

1
@efaruk मुझे यकीन नहीं है कि आप क्या कहना चाह रहे हैं .. लेकिन जब तक आप सहमत हैं कि WebAPI में सिंक पर सिंक का उपयोग करने का कोई कारण नहीं है तो यह ठीक है।
i3arnon

@efaruk "सिंक पर async" (यानी await Task.Run(() => CPUIntensive())) asp.net में बेकार है। आप ऐसा करने से कुछ हासिल नहीं करते हैं। आप बस एक थ्रेडपूल थ्रेड को दूसरे पर कब्जा करने के लिए जारी कर रहे हैं। यह केवल सिंक्रोनस विधि को कॉल करने की तुलना में कम कुशल है।

1
@efaruk नहीं, यह उचित नहीं है। आप उदाहरण स्वतंत्र कार्यों को क्रमिक रूप से चलाते हैं। सिफारिशें करने से पहले आपको वास्तव में asyc / इंतजार करने की आवश्यकता है। आपको await Task.WhenAllसमानांतर में निष्पादित करने के लिए उपयोग करने की आवश्यकता होगी ।
सोरेन बोइसन

1
@efaruk As Boisen बताते हैं, आपका उदाहरण केवल इन तुल्यकालिक तरीकों को क्रमिक रूप से कॉल करने के शीर्ष पर कोई मूल्य नहीं जोड़ता है। Task.Runयदि आप अपने लोड को कई थ्रेड्स पर समानांतर करना चाहते हैं, तो आप इसका उपयोग कर सकते हैं , लेकिन यह "सिंक पर सिंक" का मतलब नहीं है। "async पर सिंक" संदर्भ एक सिंक्रोनस पर एक आवरण के रूप में एक async विधि बना रहा है। आप मेरे जवाब में बोली में देख सकते हैं।
१.३४

1

एक दृष्टिकोण हो सकता है (मैंने इसे ग्राहक अनुप्रयोगों में सफलतापूर्वक उपयोग किया है) वर्कर थ्रेड के साथ एक लंबी सेवा चलाने के लिए एक Windows सेवा है, और फिर IIS में थ्रेड्स को खाली करने के लिए तब तक करें जब तक कि ब्लॉकिंग ऑपरेशन पूरा न हो जाए: ध्यान दें, यह अनुमान है परिणाम तालिका में रखे जाते हैं (जॉब द्वारा पहचानी गई पंक्तियाँ) और उपयोग के कुछ घंटे बाद उन्हें साफ करने वाली एक क्लीनर प्रक्रिया।

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

// GET api/<controller>
[HttpGet]
[Route("pharmacies/{pharmacyId}/page/{page}/{filter?}")]
public async Task<CartTotalsDTO> GetProductsWithHistory(Guid pharmacyId, int page, string filter = null ,[FromUri] bool refresh = false)
{
        var jobID = Guid.NewGuid().ToString()
        var job = new Job
        {
            Id = jobId,
            jobType = "GetProductsWithHistory",
            pharmacyId = pharmacyId,
            page = page,
            filter = filter,
            Created = DateTime.UtcNow,
            Started = null,
            Finished = null,
            User =  {{extract user id in the normal way}}
        };
        jobService.CreateJob(job);

        var timeout = 10*60*1000; //10 minutes
        Stopwatch sw = new Stopwatch();
        sw.Start();
        bool responseReceived = false;
        do
        {
            //wait for the windows service to process the job and build the results in the results table
            if (jobService.GetJob(jobId).Finished == null)
            {
                if (sw.ElapsedMilliseconds > timeout ) throw new TimeoutException();
                await Task.Delay(2000);
            }
            else
            {
                responseReceived = true;
            }
        } while (responseReceived == false);

    //this fetches the results from the temporary results table
    return jobService.GetProductsWithHistory(jobId);
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.