समानांतर में दो async कार्य चलाएं और .NET 4.5 में परिणाम एकत्र करें


116

मैं कुछ समय के लिए कोशिश कर रहा हूँ मुझे लगा कि कुछ आसान होगा। .NET 4.5 के साथ काम करना

मैं एक ही समय में दो लंबे चलने वाले कार्यों को बंद करना चाहता हूं और
सर्वोत्तम C # 4.5 (RTM) तरीके से परिणाम एकत्र करता हूं

निम्नलिखित काम करता है लेकिन मुझे यह पसंद नहीं है क्योंकि:

  • मैं Sleepएक async विधि बनना चाहता हूँ ताकि यह awaitअन्य विधियाँ हो
  • यह बस के साथ अनाड़ी लग रहा है Task.Run()
  • मुझे नहीं लगता कि यह किसी भी नई भाषा की विशेषताओं का उपयोग कर रहा है!

काम कोड:

public static void Go()
{
    Console.WriteLine("Starting");

    var task1 = Task.Run(() => Sleep(5000));    
    var task2 = Task.Run(() => Sleep(3000));

    int totalSlept = task1.Result + task2.Result;

    Console.WriteLine("Slept for a total of " + totalSlept + " ms");
}

private static int Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    Thread.Sleep(ms);
    Console.WriteLine("Sleeping for " + ms + " FINISHED");
    return ms;
}

गैर काम कोड:

अपडेट: यह वास्तव में काम करता है और इसे करने का सही तरीका है, एकमात्र समस्या यह है कि Thread.Sleep

यह कोड काम नहीं करता है क्योंकि कॉल Sleep(5000)तुरंत चलने वाले कार्य को शुरू Sleep(1000)करता है इसलिए जब तक यह पूरा नहीं हो जाता तब तक नहीं चलता है। हालांकि यह सच Sleepहै asyncऔर मैं बहुत जल्द इसका उपयोग awaitया कॉल नहीं कर रहा हूं .Result

मैंने सोचा कि शायद Task<T>एक asyncविधि को कॉल करके नॉन-रनिंग प्राप्त करने का एक तरीका है, इसलिए मैं फिर Start()दो कार्यों को कॉल कर सकता हूं , लेकिन मैं यह पता नहीं लगा सकता कि कैसे Task<T>एक async विधि को कॉल करने से प्राप्त किया जाए ।

public static void Go()
{
    Console.WriteLine("Starting");

    var task1 = Sleep(5000);    // blocks
    var task2 = Sleep(1000);

    int totalSlept = task1.Result + task2.Result;

    Console.WriteLine("Slept for " + totalSlept + " ms");
}

private static async Task<int> Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    Thread.Sleep(ms);
    return ms;
}

ध्यान दें: गोइंग ऐसिंक मेथड से कोई फर्क नहीं पड़ता
सिमोन_विवर सेप

3
ब्लॉक इसलिए task1.Resultनहीं हो रहा है var task1 = Sleep(5000)क्योंकि बिना किसी प्रतीक्षा कीवर्ड के आपकी स्लीप विधि समकालिक है।
अरविस

जवाबों:


86

आपको Async प्रोग्रामिंग के लिए Sleep के बजाय Task.Delay का उपयोग करना चाहिए और फिर कार्य परिणामों को संयोजित करने के लिए Task.WhenAll का उपयोग करना चाहिए। कार्य समानांतर में चलेंगे।

public class Program
    {
        static void Main(string[] args)
        {
            Go();
        }
        public static void Go()
        {
            GoAsync();
            Console.ReadLine();
        }
        public static async void GoAsync()
        {

            Console.WriteLine("Starting");

            var task1 = Sleep(5000);
            var task2 = Sleep(3000);

            int[] result = await Task.WhenAll(task1, task2);

            Console.WriteLine("Slept for a total of " + result.Sum() + " ms");

        }

        private async static Task<int> Sleep(int ms)
        {
            Console.WriteLine("Sleeping for {0} at {1}", ms, Environment.TickCount);
            await Task.Delay(ms);
            Console.WriteLine("Sleeping for {0} finished at {1}", ms, Environment.TickCount);
            return ms;
        }
    }

11
यह एक शानदार जवाब है ... लेकिन मुझे लगा कि यह गलत जवाब था जब तक मैंने इसे नहीं चलाया। तब मैं समझ गया। यह वास्तव में 5 सेकंड में निष्पादित करता है। कार्य टास्क के लिए तुरंत इंतजार नहीं करना है, इसके बजाय टास्क पर इंतजार करना होगा।
टिम लोवेल-स्मिथ

113
async Task<int> LongTask1() { 
  ...
  return 0; 
}

async Task<int> LongTask2() { 
  ...
  return 1; 
}

...
{
   Task<int> t1 = LongTask1();
   Task<int> t2 = LongTask2();
   await Task.WhenAll(t1,t2);
   //now we have t1.Result and t2.Result
}

2
I +1 क्योंकि आप t1, t2 को टास्क घोषित करते हैं, जो कि सही तरीका है।
लघु

12
मेरा मानना ​​है कि इस समाधान के लिए गो विधि भी अनिवार्य है, जिसका अर्थ है कि यह अतुल्यकालिक होने की क्षमता को उजागर करता है। यदि आप कुछ और पूछना चाहते हैं, जैसे कि कॉलर का Goतरीका समकालिक है, लेकिन दो स्वतंत्र कार्यों को एसिंक्रोनस रूप से पूरा करना चाहता है (यानी न तो दूसरे से पहले पूरा करने की आवश्यकता है, लेकिन दोनों को निष्पादित होने से पहले पूरा करना Task.WaitAllहोगा ) तो बेहतर होगा, और डॉन इस प्रकार प्रतीक्षित खोजशब्द की आवश्यकता नहीं है, इस प्रकार कॉल Goकरने की विधि की आवश्यकता नहीं है। न तो दृष्टिकोण बेहतर है, यह सिर्फ एक लक्ष्य है कि आपका लक्ष्य क्या है।
एरोनल्स

1
शून्य मेथोड: async void LongTask1() {...}कोई टास्क नहीं है। संपत्ति का अनुपात। ऐसे मामले में T के बिना टास्क का उपयोग करें async Task LongTask1():।
अरविस

मुझे किसी एक कार्य से परिणाम नहीं मिला। इसलिए मैंने इसे बदल दिया Task<TResult> t1 = LongTask1();और अब मुझे मिलता है t1.Result<TResult>आपके परिणाम का वापसी प्रकार है। इसके लिए आपको return <TResult>अपने तरीके की आवश्यकता होगी ।
गिल्लू

1
यह ध्यान देने योग्य हो सकता है कि यदि आप कुछ सरल चीजें कर रहे हैं और अतिरिक्त t1और t2चर नहीं चाहते हैं , तो आप उपयोग कर सकते हैं new Task(...)। उदाहरण के लिए int int1 = 0; await Task.WhenAll(new Task(async () => { int1 = await LongTask1(); }));:। इस दृष्टिकोण का एक कैच यह है कि कंपाइलर यह नहीं पहचान पाएगा कि वैरिएबल को असाइन किया गया है और इसे अप्रमाणित मानेंगे यदि आप इसे प्रारंभिक मूल्य नहीं देते हैं।
रॉबर्ट डेनिस

3

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

मैं मान रहा हूं Thread.Sleepकि आप वास्तव में क्या करना चाहते हैं, इसका एक सरलीकरण है। क्या आपके वास्तविक कार्यान्वयन को async विधियों के रूप में कोडित किया जा सकता है?

यदि आपको कई सिंक्रोनस ब्लॉकिंग कॉल चलाने की आवश्यकता है, तो मुझे लगता है कि कहीं और देखो!


धन्यवाद रिचर्ड - हाँ यह उम्मीद के
मुताबिक

तो कैसे async चलाने के लिए? मैं अनुप्रयोग है कि फ़ाइल के लिए फ़ाइल स्विचिंग और प्रतीक्षा की बहुत कुछ कर, चारों ओर 5 सेकंड, और फिर एक और प्रक्रिया है, जब मैं "जब के लिए सभी" यह पहली बार पहले, तो दूसरा, भले ही मैंने कहा चलाएँ: var x = y(), और नहीं var x=await y()या y().wait()अभी भी यह अभी तक सभी तरह से प्रतीक्षा करें, और यदि async संभाल नहीं है कि अपने आप से, मुझे क्या करना चाहिए? ध्यान दें कि y को async से सजाया गया है, और मुझे उम्मीद है कि यह सब के भीतर जब सभी को करना है, तो यह सही नहीं है कि इसे कहाँ सौंपा गया है, संपादित करें: मैं बस जब मेरे साथी ने कहा, चलो कोशिश करते हैं Task.Factory, और उन्होंने कहा कि जब मैंने ब्रेक आउट किया तो यह काम किया इस वर्ग का पक्ष
डेडमैनएन

2

इस बिंदु का उत्तर देने के लिए:

मैं चाहता हूं कि नींद एक एसिंक्स विधि हो, ताकि यह अन्य तरीकों का इंतजार कर सके

आप शायद Sleepइस तरह से फ़ंक्शन को फिर से लिख सकते हैं :

private static async Task<int> Sleep(int ms)
{
    Console.WriteLine("Sleeping for " + ms);
    var task = Task.Run(() => Thread.Sleep(ms));
    await task;
    Console.WriteLine("Sleeping for " + ms + "END");
    return ms;
}

static void Main(string[] args)
{
    Console.WriteLine("Starting");

    var task1 = Sleep(2000);
    var task2 = Sleep(1000);

    int totalSlept = task1.Result +task2.Result;

    Console.WriteLine("Slept for " + totalSlept + " ms");
    Console.ReadKey();
}

इस कोड को चलाने से उत्पादन होगा:

Starting
Sleeping for 2000
Sleeping for 1000
*(one second later)*
Sleeping for 1000END
*(one second later)*
Sleeping for 2000END
Slept for 3000 ms

2

यह सप्ताह के अंत में करें!

    public async void Go()
    {
        Console.WriteLine("Start fosterage...");

        var t1 = Sleep(5000, "Kevin");
        var t2 = Sleep(3000, "Jerry");
        var result = await Task.WhenAll(t1, t2);

        Console.WriteLine($"My precious spare time last for only {result.Max()}ms");
        Console.WriteLine("Press any key and take same beer...");
        Console.ReadKey();
    }

    private static async Task<int> Sleep(int ms, string name)
    {
            Console.WriteLine($"{name} going to sleep for {ms}ms :)");
            await Task.Delay(ms);
            Console.WriteLine("${name} waked up after {ms}ms :(";
            return ms;
    }

0

इस लेख ने बहुत सी चीजों को समझाने में मदद की। यह अक्सर पूछे जाने वाले प्रश्न शैली में है।

Async / Await FAQ

यह हिस्सा बताता है कि Thread.Sleepएक ही मूल धागे पर क्यों चलता है - मेरे प्रारंभिक भ्रम की ओर जाता है।

क्या "async" कीवर्ड थ्रेडपूल को कतार में भेजने के लिए एक विधि के आह्वान का कारण बनता है? एक नया धागा बनाने के लिए? मंगल ग्रह के लिए एक रॉकेट जहाज लॉन्च करने के लिए?

नहीं। और नहीं। पिछले प्रश्न देखें। "Async" कीवर्ड कंपाइलर को इंगित करता है कि "इंतजार" विधि के अंदर इस्तेमाल किया जा सकता है, जैसे कि विधि एक प्रतीक्षा बिंदु पर निलंबित कर सकती है और इसका निष्पादन asynchronously फिर से शुरू हो जाता है जब प्रतीक्षित उदाहरण पूरा होता है। यही कारण है कि संकलक एक चेतावनी जारी करता है यदि "async" के रूप में चिह्नित विधि के अंदर "प्रतीक्षा" नहीं है।

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