समानांतर में कई async सेवाओं को कॉल करना


17

मेरे पास कुछ async REST सेवाएँ हैं जो एक दूसरे पर निर्भर नहीं हैं। यह है कि Service1 की प्रतिक्रिया का "इंतजार" करते समय, मैं Service2, Service3 और इतने पर कॉल कर सकता हूं।

उदाहरण के लिए, नीचे दिया गया कोड देखें:

var service1Response = await HttpService1Async();
var service2Response = await HttpService2Async();

// Use service1Response and service2Response

अब, service2Responseनिर्भर नहीं है service1Responseऔर उन्हें स्वतंत्र रूप से लाया जा सकता है। इसलिए, मुझे दूसरी सेवा को कॉल करने के लिए पहली सेवा की प्रतिक्रिया का इंतजार करने की कोई आवश्यकता नहीं है।

मुझे नहीं लगता कि मैं Parallel.ForEachयहां उपयोग कर सकता हूं क्योंकि यह सीपीयू बाउंड ऑपरेशन नहीं है।

इन दो ऑपरेशनों को समानांतर में कॉल करने के लिए, क्या मैं उपयोग को कॉल कर सकता हूं Task.WhenAll? एक मुद्दा जो मैं देख रहा हूं Task.WhenAllवह यह है कि यह परिणाम नहीं देता है। परिणाम प्राप्त करने के लिए मैं कॉल करने के task.Resultबाद कॉल कर सकता हूं Task.WhenAll, क्योंकि सभी कार्य पहले से ही पूरे हो चुके हैं और सभी को हमें प्रतिक्रिया प्राप्त करने की आवश्यकता है?

नमूना कोड:

var task1 = HttpService1Async();
var task2 = HttpService2Async();

await Task.WhenAll(task1, task2)

var result1 = task1.Result;
var result2 = task2.Result;

// Use result1 and result2

क्या यह कोड प्रदर्शन के मामले में पहले से बेहतर है? कोई अन्य दृष्टिकोण जिसका मैं उपयोग कर सकता हूं?


I do not think I can use Parallel.ForEach here since it is not CPU bound operation- मैं वहां तर्क नहीं देखता। कॉनक्यूरेन्सी कंसीडर है।
रॉबर्ट हार्वे

3
@RobertHarvey मुझे लगता है कि चिंता इस बात का है, इस संदर्भ में, Parallel.ForEachनए धागे स्पॉन async awaitकरेंगे जबकि एक ही धागे पर सब कुछ करेंगे।
मेटाफ़ाइट

@Ankit यह एक निर्भर करता है जब यह आपके कोड को ब्लॉक करने के लिए उपयुक्त है। आपका दूसरा उदाहरण तब तक अवरुद्ध होगा जब तक दोनों प्रतिक्रियाएं तैयार नहीं हो जातीं। आपका पहला उदाहरण, संभवतः, केवल तभी तार्किक रूप से अवरुद्ध होगा जब कोड awaitतैयार होने से पहले प्रतिक्रिया ( ) का उपयोग करने का प्रयास करेगा ।
मेटाफ़ाइट

यदि आप दोनों सेवा प्रतिक्रियाओं का उपभोग करने वाले कोड का कम सार उदाहरण प्रदान करते हैं, तो आपको अधिक संतोषजनक जवाब देना आसान हो सकता है।
मेटाफ़ाइट

मैं अपने दूसरे उदाहरण में @MetaFight कर रहा हूँ WhenAllइससे पहले कि मैं ऐसा Resultविचार है कि यह सभी कार्य से पहले .Result कहा जाता है पूरा करता है। चूंकि, Task.Result कॉलिंग थ्रेड को ब्लॉक करता है, इसलिए मैं मानता हूं कि यदि मैं कार्य पूरा होने के बाद इसे कॉल करता हूं तो यह तुरंत परिणाम देगा। मैं समझ को मान्य करना चाहता हूं।
अंकित विजय

जवाबों:


17

एक मुद्दा मैं टास्क का उपयोग कर रहा हूँ। जब कोई भी यह परिणाम नहीं देता है

लेकिन यह करता है परिणाम वापस। वे सभी एक सामान्य प्रकार के एक सरणी में होंगे, इसलिए परिणामों का उपयोग करने के लिए हमेशा उपयोगी नहीं होता है, आपको उस आइटम को उस सरणी में ढूंढना होगा जो Taskउस परिणाम से मेल खाती है जिसके लिए आप परिणाम चाहते हैं, और संभावित रूप से इसे उसके लिए डालें वास्तविक प्रकार, इसलिए यह इस संदर्भ में सबसे आसान / सबसे पठनीय दृष्टिकोण नहीं हो सकता है, लेकिन जब आप हर कार्य से सभी परिणाम प्राप्त करना चाहते हैं, और सामान्य प्रकार वह प्रकार है जिसे आप उनके साथ व्यवहार करना चाहते हैं, तो यह बहुत अच्छा है ।

रिजल्ट लाने के लिए मैं टास्क को कॉल कर सकता हूं। टास्क कॉल करने के बाद रिजल्ट। जब भी सभी कार्य पहले से ही पूरे हो जाते हैं और सभी को हमें रिस्पांस लाने की आवश्यकता होती है?

हां, आप ऐसा कर सकते हैं। आप awaitउन्हें भी कर सकते हैं ( awaitकिसी भी दोषपूर्ण कार्य में अपवाद को रद्द कर देंगे, जबकि Resultएक समग्र अपवाद को फेंक देंगे, लेकिन अन्यथा यह वही होगा)।

क्या यह कोड प्रदर्शन के मामले में पहले से बेहतर है?

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

किसी भी अन्य दृष्टिकोण का मैं उपयोग कर सकता हूं?

अगर यह आपके लिए महत्वपूर्ण नहीं है कि आप सभी अपवादों के बारे में जानते हैं, जो आप केवल पहले वाले के बजाय समानांतर में कर रहे हैं, तो आप awaitबिना किसी कार्य के बस काम कर सकते हैं WhenAll। केवल एक चीज WhenAllआपको देता है कि आप AggregateExceptionहर दोषपूर्ण कार्य से हर एक अपवाद के साथ हैं, बजाय जब आप पहले दोषपूर्ण कार्य को मारते हैं। यह उतना ही सरल है:

var task1 = HttpService1Async();
var task2 = HttpService2Async();

var result1 = await task1;
var result2 = await task2;

यह समवर्ती कार्यों को अकेले समानांतर में नहीं चलने दे रहा है। आप अनुक्रमिक क्रम में प्रत्येक कार्य के पूरा होने की प्रतीक्षा कर रहे हैं। यदि आप निष्पादक कोड की परवाह नहीं करते हैं तो पूरी तरह से ठीक है।
रिक ओ'शिथ

3
@ RickO'Shea यह क्रमिक रूप से संचालन शुरू करता है। यह पहला ऑपरेशन शुरू करने के बाद दूसरा ऑपरेशन शुरू करेगा । लेकिन अतुल्यकालिक संचालन शुरू करना मूल रूप से तात्कालिक होना चाहिए (यदि ऐसा नहीं है, तो यह वास्तव में अतुल्यकालिक नहीं है, और यह उस पद्धति में एक बग है)। एक शुरू करने के बाद, और फिर दूसरा, यह पहले खत्म होने के बाद तक जारी नहीं रहेगा, और फिर दूसरा खत्म। चूंकि दूसरा शुरू करने से पहले पहले खत्म होने का इंतजार नहीं किया जाता है, इसलिए कुछ भी उन्हें समवर्ती रूप से चलाने से रोक रहा है (जो समानांतर में चलने के समान है)।
सेवी

@ मुझे लगता है कि यह सच नहीं है। मैंने दो async ऑपरेशंस के अंदर लॉगिंग को जोड़ा, जो लगभग एक सेकंड में (दोनों http कॉल करते हैं) और फिर उन्हें बुलाया जैसा कि आपने सुझाव दिया है, और निश्चित रूप से पर्याप्त task1 शुरू हुआ और समाप्त हो गया और फिर task2 शुरू और समाप्त हो गया।
मैट फ्रियर

@MattFrear तब विधि वास्तव में अतुल्यकालिक नहीं थी। यह समकालिक था। परिभाषा के अनुसार , एक अतुल्यकालिक विधि वास्तव में वापस लौटने वाली है, बजाय ऑपरेशन के बाद लौटने के बजाय वास्तव में समाप्त हो गई है।
सेविला

@ परिभाषा के अनुसार, प्रतीक्षा का अर्थ है कि आप प्रतीक्षा करेंगे जब तक कि अगली पंक्ति को निष्पादित करने से पहले एस्क्वायरोनस कार्य पूरा न हो जाए। है ना?
मैट फियर

0

यहाँ विस्तार विधि है जो सेमाफोरस्लीम का उपयोग करती है और अधिकतम समानता सेट करने की अनुमति देती है

    /// <summary>
    /// Concurrently Executes async actions for each item of <see cref="IEnumerable<typeparamref name="T"/>
    /// </summary>
    /// <typeparam name="T">Type of IEnumerable</typeparam>
    /// <param name="enumerable">instance of <see cref="IEnumerable<typeparamref name="T"/>"/></param>
    /// <param name="action">an async <see cref="Action" /> to execute</param>
    /// <param name="maxDegreeOfParallelism">Optional, An integer that represents the maximum degree of parallelism,
    /// Must be grater than 0</param>
    /// <returns>A Task representing an async operation</returns>
    /// <exception cref="ArgumentOutOfRangeException">If the maxActionsToRunInParallel is less than 1</exception>
    public static async Task ForEachAsyncConcurrent<T>(
        this IEnumerable<T> enumerable,
        Func<T, Task> action,
        int? maxDegreeOfParallelism = null)
    {
        if (maxDegreeOfParallelism.HasValue)
        {
            using (var semaphoreSlim = new SemaphoreSlim(
                maxDegreeOfParallelism.Value, maxDegreeOfParallelism.Value))
            {
                var tasksWithThrottler = new List<Task>();

                foreach (var item in enumerable)
                {
                    // Increment the number of currently running tasks and wait if they are more than limit.
                    await semaphoreSlim.WaitAsync();

                    tasksWithThrottler.Add(Task.Run(async () =>
                    {
                        await action(item).ContinueWith(res =>
                        {
                            // action is completed, so decrement the number of currently running tasks
                            semaphoreSlim.Release();
                        });
                    }));
                }

                // Wait for all tasks to complete.
                await Task.WhenAll(tasksWithThrottler.ToArray());
            }
        }
        else
        {
            await Task.WhenAll(enumerable.Select(item => action(item)));
        }
    }

नमूना उपयोग:

await enumerable.ForEachAsyncConcurrent(
    async item =>
    {
        await SomeAsyncMethod(item);
    },
    5);

-2

आप या तो उपयोग कर सकते हैं

Parallel.Invoke(() =>
{
    HttpService1Async();
},
() =>
{   
    HttpService2Async();
});

या

Task task1 = Task.Run(() => HttpService1Async());
Task task2 = Task.Run(() => HttpService2Async());

//If you wish, you can wait for a particular task to return here like this:
task1.Wait();

क्यों घटता है?
user1451111
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.