Προγραμματισμός

Πότε να χρησιμοποιήσετε το Task.WaitAll εναντίον Task.WhenAll στο .NET

Το TPL (Task Parallel Library) είναι ένα από τα πιο ενδιαφέροντα νέα χαρακτηριστικά που προστέθηκαν στις πρόσφατες εκδόσεις του .NET framework. Οι μέθοδοι Task.WaitAll και Task.WhenAll είναι δύο σημαντικές και συχνά χρησιμοποιούμενες μέθοδοι στο TPL.

Το Task.WaitAll αποκλείει το τρέχον νήμα έως ότου ολοκληρωθούν όλες οι άλλες εργασίες. Η μέθοδος Task.WhenAll χρησιμοποιείται για τη δημιουργία μιας εργασίας που θα ολοκληρωθεί εάν και μόνο εάν έχουν ολοκληρωθεί όλες οι άλλες εργασίες.

Έτσι, εάν χρησιμοποιείτε το Task.WhenAll θα λάβετε ένα αντικείμενο εργασίας που δεν είναι πλήρες. Ωστόσο, δεν θα μπλοκάρει αλλά θα επιτρέψει την εκτέλεση του προγράμματος. Αντίθετα, η κλήση της μεθόδου Task.WaitAll αποκλείει και περιμένει να ολοκληρωθούν όλες οι άλλες εργασίες.

Ουσιαστικά, Task.WhenAll θα σας δώσει μια εργασία που δεν είναι πλήρης, αλλά μπορείτε να χρησιμοποιήσετε το ContinueWith μόλις οι συγκεκριμένες εργασίες έχουν ολοκληρώσει την εκτέλεσή τους. Σημειώστε ότι ούτε το Task.WhenAll ούτε το Task.WaitAll θα εκτελέσουν τις εργασίες. δηλαδή, δεν ξεκινούν εργασίες με αυτές τις μεθόδους. Δείτε πώς χρησιμοποιείται το ContinueWith με το Task.WhenAll:

Task.WhenAll (taskList) .ContinueWith (t => {

// γράψτε τον κωδικό σας εδώ

});

Όπως αναφέρει η τεκμηρίωση της Microsoft, το Task.WhenAll "δημιουργεί μια εργασία που θα ολοκληρωθεί όταν όλα τα αντικείμενα Task σε μια αναρίθμητη συλλογή έχουν ολοκληρωθεί."

Task.WhenAll εναντίον Task.WaitAll

Επιτρέψτε μου να εξηγήσω τη διαφορά μεταξύ αυτών των δύο μεθόδων με ένα απλό παράδειγμα. Ας υποθέσουμε ότι έχετε μια εργασία που εκτελεί κάποια δραστηριότητα με το νήμα διεπαφής χρήστη - ας πούμε, κάποια κινούμενα σχέδια πρέπει να εμφανίζονται στο περιβάλλον χρήστη. Τώρα, εάν χρησιμοποιείτε το Task.WaitAll, η διεπαφή χρήστη θα αποκλειστεί και δεν θα ενημερωθεί έως ότου ολοκληρωθούν όλες οι σχετικές εργασίες και αποδεσμευτεί το μπλοκ. Ωστόσο, εάν χρησιμοποιείτε το Task.When Όλα στην ίδια εφαρμογή, το νήμα διεπαφής χρήστη δεν θα αποκλειστεί και θα ενημερωθεί ως συνήθως.

Ποια από αυτές τις μεθόδους πρέπει να χρησιμοποιείτε πότε; Λοιπόν, μπορείτε να χρησιμοποιήσετε το WaitAll όταν η πρόθεση αποκλείει συγχρόνως για να πάρει τα αποτελέσματα. Αλλά όταν θέλετε να αξιοποιήσετε την ασύγχρονη, θα θέλατε να χρησιμοποιήσετε την παραλλαγή WhenAll. Μπορείτε να περιμένετε το Task.WhenΌλα χωρίς να χρειάζεται να αποκλείσετε το τρέχον νήμα. Ως εκ τούτου, μπορεί να θέλετε να χρησιμοποιήσετε την αναμονή με το Task.WhenAll μέσα σε μια μέθοδο async.

Ενώ το Task.WaitAll αποκλείει το τρέχον νήμα έως ότου ολοκληρωθούν όλες οι εκκρεμείς εργασίες, το Task.WhenAll επιστρέφει ένα αντικείμενο εργασίας. Task.WaitAll ρίχνει ένα AggregateException όταν μία ή περισσότερες από τις εργασίες ρίχνουν μια εξαίρεση. Όταν μία ή περισσότερες εργασίες ρίχνουν μια εξαίρεση και περιμένετε τη μέθοδο Task.WhenAll, ξετυλίγει το AggregateException και επιστρέφει μόνο την πρώτη.

Αποφύγετε τη χρήση του Task. Εκτελέστε βρόχους

Μπορείτε να χρησιμοποιήσετε εργασίες όταν θέλετε να εκτελείτε ταυτόχρονες δραστηριότητες. Εάν χρειάζεστε υψηλό βαθμό παραλληλισμού, οι εργασίες δεν είναι ποτέ καλή επιλογή. Συνιστάται πάντοτε να αποφεύγετε τη χρήση νημάτων μπιλιάρδου στο ASP.Net. Ως εκ τούτου, θα πρέπει να αποφύγετε τη χρήση Task.Run ή Task.factory.StartNew στο ASP.Net.

Task.Run θα πρέπει πάντα να χρησιμοποιείται για δεσμευμένο κώδικα CPU. Το Task.Run δεν είναι καλή επιλογή στις εφαρμογές ASP.Net ή σε εφαρμογές που αξιοποιούν το χρόνο εκτέλεσης ASP.Net, καθώς απλώς εκφορτώνει την εργασία σε ένα νήμα ThreadPool. Εάν χρησιμοποιείτε ASP.Net Web API, το αίτημα θα χρησιμοποιεί ήδη ένα νήμα ThreadPool. Ως εκ τούτου, εάν χρησιμοποιείτε το Task.Run στην εφαρμογή σας ASP.Net Web API, περιορίζετε απλώς την επεκτασιμότητα κατεβάζοντας την εργασία σε άλλο νήμα εργαζομένου χωρίς κανένα λόγο.

Σημειώστε ότι υπάρχει ένα μειονέκτημα στη χρήση του Task.Run σε βρόχο. Εάν χρησιμοποιείτε τη μέθοδο Task.Run μέσα σε ένα βρόχο, θα δημιουργηθούν πολλές εργασίες - μία για κάθε μονάδα εργασίας ή επανάληψη. Ωστόσο, εάν χρησιμοποιείτε το Parallel.ForEach αντί για τη χρήση του Task. Εκτελέστε μέσα σε ένα βρόχο, δημιουργείται ένα Partitioner για να αποφύγετε τη δημιουργία περισσότερων εργασιών για την εκτέλεση της δραστηριότητας από ό, τι απαιτείται. Αυτό μπορεί να βελτιώσει σημαντικά την απόδοση, καθώς μπορείτε να αποφύγετε πάρα πολλούς διακόπτες περιβάλλοντος και να εξακολουθείτε να αξιοποιείτε πολλούς πυρήνες στο σύστημά σας.

Πρέπει να σημειωθεί ότι το Parallel.ForEach χρησιμοποιεί το Partitioner εσωτερικά για να διανείμει τη συλλογή σε αντικείμενα εργασίας. Παρεμπιπτόντως, αυτή η διανομή δεν συμβαίνει για κάθε εργασία στη λίστα αντικειμένων, αλλά συμβαίνει ως παρτίδα. Αυτό μειώνει την επιβάρυνση και συνεπώς βελτιώνει την απόδοση. Με άλλα λόγια, εάν χρησιμοποιείτε Task.Run ή Task.Factory.StartNew μέσα σε ένα βρόχο, θα δημιουργούσαν νέες εργασίες ρητά για κάθε επανάληψη του βρόχου. Parallel.ForEach είναι πολύ πιο αποτελεσματικό, διότι θα βελτιστοποιήσει την εκτέλεση διανέμοντας το φορτίο εργασίας στους πολλαπλούς πυρήνες του συστήματός σας.