कई async कार्य चला रहा है और उन सभी के पूरा होने की प्रतीक्षा कर रहा है


265

मुझे एक सांत्वना अनुप्रयोग में कई async कार्य चलाने की आवश्यकता है, और आगे की प्रक्रिया से पहले उन सभी को पूरा करने की प्रतीक्षा करें।

वहाँ बहुत से लेख हैं, लेकिन मुझे लगता है कि मैं जितना अधिक पढ़ता हूं उतना अधिक भ्रमित होता हूं। मैंने टास्क लाइब्रेरी के मूल सिद्धांतों को पढ़ा और समझा है, लेकिन मैं स्पष्ट रूप से एक लिंक को याद कर रहा हूं।

मैं समझता हूं कि कार्यों को चेन करना संभव है ताकि वे एक और पूर्ण होने के बाद शुरू करें (जो कि मेरे द्वारा पढ़े गए सभी लेखों के लिए बहुत ज्यादा परिदृश्य है), लेकिन मैं चाहता हूं कि मेरे सभी कार्य एक ही समय में चल रहे हों, और मैं एक बार जानना चाहता हूं वे सब पूरे हो गए।

इस तरह के परिदृश्य के लिए सबसे सरल कार्यान्वयन क्या है?

जवाबों:


440

दोनों उत्तरों में प्रतीक्षा का उल्लेख नहीं था Task.WhenAll:

var task1 = DoWorkAsync();
var task2 = DoMoreWorkAsync();

await Task.WhenAll(task1, task2);

के बीच मुख्य अंतर है Task.WaitAllऔर Task.WhenAllयह है कि पूर्व ब्लॉक करेगा ( Waitएक ही कार्य पर उपयोग करने के समान ), जबकि बाद वाला नहीं होगा और इंतजार किया जा सकता है, जब तक कि सभी कार्य समाप्त नहीं हो जाते।

और अधिक, अपवाद हैंडलिंग अलग है:

Task.WaitAll:

टास्क इंस्टेंसेस में से कम से कम एक को रद्द कर दिया गया था-और- कम से कम एक टास्क इंस्टेंस के निष्पादन के दौरान एक अपवाद फेंक दिया गया था। यदि किसी कार्य को रद्द कर दिया गया था, तो एग्रीगेटएक्सपेप्शन में उसके आंतरिक अपवाद संग्रह में एक OperationCanceledException शामिल है।

Task.WhenAll:

यदि आपूर्ति किए गए कार्यों में से कोई भी एक दोषपूर्ण स्थिति में पूरा होता है, तो लौटाया गया कार्य भी एक दोषपूर्ण स्थिति में पूरा होगा, जहां इसके अपवादों में आपूर्ति किए गए कार्यों में से प्रत्येक से अलिखित अपवादों के समूह का एकत्रीकरण होगा।

यदि आपूर्ति किए गए कार्यों में से कोई भी दोषपूर्ण नहीं है, लेकिन उनमें से कम से कम एक को रद्द कर दिया गया था, तो लौटाया गया कार्य रद्द की गई स्थिति में समाप्त हो जाएगा।

यदि कोई भी कार्य दोषपूर्ण नहीं है और कोई भी कार्य रद्द नहीं किया गया है, तो परिणामस्वरूप कार्य RanToCompletion स्थिति में समाप्त हो जाएगा। यदि आपूर्ति की गई सरणी / गणना करने योग्य कोई कार्य नहीं है, तो लौटाया गया कार्य तुरंत कॉल करने पर वापस लौटने से पहले एक RanToCompletion राज्य में संक्रमण करेगा।


4
जब मैं यह कोशिश करता हूं तो मेरे कार्य क्रमिक रूप से चलते हैं? क्या प्रत्येक कार्य को व्यक्तिगत रूप से पहले शुरू करना होगा await Task.WhenAll(task1, task2);?
Zapnologica

4
@Zapnologica Task.WhenAllआपके लिए कार्य प्रारंभ नहीं करता है। आपको उन्हें "गर्म" प्रदान करना होगा, जिसका अर्थ पहले से ही शुरू हो गया है।
युवल इट्ज़चकोव

2
ठीक। यह समझ आता है। तो आपका उदाहरण क्या होगा? क्योंकि आपने उन्हें शुरू नहीं किया है?
ज़पानोलॉजिक

2
@YuvalItzchakov आपको बहुत बहुत धन्यवाद! यह बहुत सरल है लेकिन इसने आज मुझे बहुत मदद की! कम से कम +1000 का मूल्य है
डैनियल ड्यूक

1
@ पियरे मैं पीछा नहीं कर रहा हूँ। StartNewउन सभी पर अतुल्यकालिक रूप से प्रतीक्षा करते हुए नए कार्यों का क्या और कताई करना है?
युवल इट्ज़चकोव

106

आप कई कार्य बना सकते हैं जैसे:

List<Task> TaskList = new List<Task>();
foreach(...)
{
   var LastTask = new Task(SomeFunction);
   LastTask.Start();
   TaskList.Add(LastTask);
}

Task.WaitAll(TaskList.ToArray());

48
मैं WhenAll की सिफारिश करेंगे
रवि

क्या एक ही समय में .Start () के बजाय कई नए थ्रेड्स शुरू करना संभव है।
मैट डब्ल्यू

1
@MattW नहीं, जब आप प्रतीक्षा का उपयोग करते हैं, तो यह पूरा होने की प्रतीक्षा करेगा। इस स्थिति में आप एक बहु-थ्रेडेड वातावरण बनाने में सक्षम नहीं होंगे। यही कारण है कि सभी कार्यों को लूप के अंत में इंतजार किया जाता है।
वायरस

5
भविष्य के पाठकों के लिए डाउनवोट क्योंकि यह स्पष्ट नहीं है कि यह एक अवरुद्ध कॉल है।
1

ऐसा न करने के कारणों के लिए स्वीकृत उत्तर देखें।
EL MOJO

26

मैंने देखा सबसे अच्छा विकल्प निम्नलिखित विस्तार विधि है:

public static Task ForEachAsync<T>(this IEnumerable<T> sequence, Func<T, Task> action) {
    return Task.WhenAll(sequence.Select(action));
}

इसे इस तरह से कॉल करें:

await sequence.ForEachAsync(item => item.SomethingAsync(blah));

या एक async लैम्ब्डा के साथ:

await sequence.ForEachAsync(async item => {
    var more = await GetMoreAsync(item);
    await more.FrobbleAsync();
});

26

आप उपयोग कर सकते हैं WhenAllजो एक प्रतीक्षा योग्य लौटाएगा Taskया WaitAllजिसके पास कोई वापसी प्रकार नहीं है और आगे कोड निष्पादन simular को ब्लॉक करेगा Thread.Sleepजब तक कि सभी कार्यों को पूरा नहीं किया जाता है, रद्द या दोषपूर्ण।

यहां छवि विवरण दर्ज करें

उदाहरण

var tasks = new Task[] {
    TaskOperationOne(),
    TaskOperationTwo()
};

Task.WaitAll(tasks);
// or
await Task.WhenAll(tasks);

यदि आप एक praticular क्रम में कार्यों को चलाने के लिए चाहते हैं तो आप प्रपत्र प्रेरणा प्राप्त कर सकते हैं इस anwser।


देर से पार्टी में आने के लिए क्षमा करें, लेकिन आपके पास awaitप्रत्येक ऑपरेशन के लिए क्यों है और उसी समय का उपयोग करें WaitAllया WhenAllTask[]प्रारंभ में कार्य बिना नहीं होना चाहिए await?
डे ज़ग

@dee zg आप सही हैं। ऊपर का इंतजार उद्देश्य को हरा देता है। मैं अपना उत्तर बदलूंगा और उन्हें निकालूंगा।
NtFreX

हाँ बस यही। स्पष्टीकरण के लिए धन्यवाद! (अच्छा जवाब के लिए upvote)
डे ज़ी

8

क्या आप Taskएस को चेन करना चाहते हैं , या उन्हें समानांतर तरीके से लागू किया जा सकता है?

जंजीर के लिए
बस कुछ करना पसंद है

Task.Run(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);
Task.Factory.StartNew(...).ContinueWith(...).ContinueWith(...).ContinueWith(...);

और Taskप्रत्येक में पिछले उदाहरण की जांच करना न भूलें ContinueWithक्योंकि यह दोषपूर्ण हो सकता है।

समानांतर तरीके के
लिए सबसे सरल तरीका जो मुझे आया था: Parallel.Invoke अन्यथा वहाँ है Task.WaitAllया आप WaitHandleशून्य कार्यों के लिए एक उलटी गिनती करने के लिए भी उपयोग कर सकते हैं (रुको, वहाँ एक नया वर्ग है:) CountdownEvent, या ...


3
उत्तर की सराहना करें, लेकिन आपके सुझावों को थोड़ा और समझाया जा सकता था।
डैनियल मिन्नर

@drminnaar उदाहरण के साथ msdn के लिंक के साथ अन्य स्पष्टीकरण जो आपको चाहिए? तुम भी लिंक पर क्लिक नहीं किया, क्या तुमने?
एंड्रियास नीडेरमेयर

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

@drminnaar यहाँ नहीं लिया गया अपराध, मैं बस उत्सुक हूँ :)
एंड्रियास निडरमेयर

5

इस तरह से मैं इसे एक सरणी Func <> के साथ करता हूं :

var tasks = new Func<Task>[]
{
   () => myAsyncWork1(),
   () => myAsyncWork2(),
   () => myAsyncWork3()
};

await Task.WhenAll(tasks.Select(task => task()).ToArray()); //Async    
Task.WaitAll(tasks.Select(task => task()).ToArray()); //Or use WaitAll for Sync

1
आप इसे टास्क एरे के रूप में क्यों नहीं रखते हैं?
ताल्हा तालिप Açıkgöz

1
यदि आपके सावधान नहीं @ ताल्हा-तालिप-अकीकगोज़ ने अपने कार्यों को तब निष्पादित किया है जब आप उन्हें निष्पादित करने की उम्मीद नहीं करते थे। फंक प्रतिनिधि के रूप में इसे करने से आपकी मंशा स्पष्ट होती है।
दालसॉफ्ट

5

फिर भी एक और जवाब ... लेकिन मैं आमतौर पर एक मामले में खुद को पाता हूं, जब मुझे डेटा को एक साथ लोड करने और चर में डालने की आवश्यकता होती है, जैसे:

var cats = new List<Cat>();
var dog = new Dog();

var loadDataTasks = new Task[]
{
    Task.Run(async () => cats = await LoadCatsAsync()),
    Task.Run(async () => dog = await LoadDogAsync())
};

try
{
    await Task.WhenAll(loadDataTasks);
}
catch (Exception ex)
{
    // handle exception
}

1
यदि LoadCatsAsync()और LoadDogAsync()केवल डेटाबेस कॉल वे IO- बाध्य हैं। Task.Run()सीपीयू-बाउंड काम के लिए है; यदि आप कर रहे हैं तो यह अतिरिक्त अनावश्यक ओवरहेड जोड़ता है जो डेटाबेस सर्वर से प्रतिक्रिया की प्रतीक्षा कर रहा है। युवल का स्वीकृत उत्तर आईओ-बाउंड कार्य के लिए सही तरीका है।
स्टीफन केनेडी

@StephenKennedy क्या आप स्पष्ट कर सकते हैं कि किस तरह का ओवरहेड और प्रदर्शन को कितना प्रभावित कर सकता है? धन्यवाद!
योहोर हरमादस्क्यी

यह टिप्पणी बॉक्स में संक्षेप में काफी कठिन होगा :) इसके बजाय मैं स्टीफन क्लीरी के लेख पढ़ने की सलाह देता हूं - वह इस सामान पर एक विशेषज्ञ है। यहां से शुरू करें: blog.stephencleary.com/2013/10/…
स्टीफन कैनेडी

-1

मैंने आपको यह दिखाने के लिए कोड का एक टुकड़ा तैयार किया कि इनमें से कुछ परिदृश्यों के लिए कार्य का उपयोग कैसे करें।

    // method to run tasks in a parallel 
    public async Task RunMultipleTaskParallel(Task[] tasks) {

        await Task.WhenAll(tasks);
    }
    // methode to run task one by one 
    public async Task RunMultipleTaskOneByOne(Task[] tasks)
    {
        for (int i = 0; i < tasks.Length - 1; i++)
            await tasks[i];
    }
    // method to run i task in parallel 
    public async Task RunMultipleTaskParallel(Task[] tasks, int i)
    {
        var countTask = tasks.Length;
        var remainTasks = 0;
        do
        {
            int toTake = (countTask < i) ? countTask : i;
            var limitedTasks = tasks.Skip(remainTasks)
                                    .Take(toTake);
            remainTasks += toTake;
            await RunMultipleTaskParallel(limitedTasks.ToArray());
        } while (remainTasks < countTask);
    }

1
टास्क के परिणाम कैसे मिलते हैं? उदाहरण के लिए, "पंक्तियों" (समानांतर में एन कार्यों से) को मर्ज करने के लिए डिटैजेबल में बांधें और इसे asp.net ग्रिडव्यू से बांधें?
प्रीग्टनटनकोजेरो कैब्रोन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.