Παράδειγμα φίλτρου νήματος Delphi χρησιμοποιώντας το AsyncCalls

Αυτό είναι το επόμενο δοκιμαστικό εγχείρημα μου για να δω τι βιβλιοθήκη για τους ντόπιους Delphi θα μου ταιριάζει καλύτερα για την εργασία "σάρωσης αρχείων" που θα ήθελα να επεξεργαστώ σε πολλαπλά νήματα / σε μια δεξαμενή νήματος.

Για να επαναλάβω το στόχο μου: μετασχηματίζω τη διαδοχική "σάρωση αρχείων" των αρχείων 500-2000 + από την μη σπειρωμένη προσέγγιση σε ένα σπειροειδές. Δεν θα έπρεπε να τρέχω 500 κλωστές ταυτόχρονα, έτσι θα ήθελα να χρησιμοποιήσω μια δεξαμενή νήματος. Μια ομάδα νήματος είναι μια τάξη που μοιάζει με ουρά και τροφοδοτεί έναν αριθμό από τρέχοντα νήματα με την επόμενη εργασία από την ουρά.

Η πρώτη (πολύ βασική) απόπειρα έγινε με την απλή επέκταση της κλάσης TThread και την εφαρμογή της μεθόδου Execute (ο τρισδιάστατος αναλυτής συμβολοσειρών).

Δεδομένου ότι οι Δελφοί δεν έχουν κλάση πισίνας νήματος υλοποιημένο από το κιβώτιο, στη δεύτερη προσπάθειά μου, προσπάθησα να χρησιμοποιήσω το OmniThreadLibrary από τον Primoz Gabrijelcic.

Το OTL είναι φανταστικό, έχει πολλούς τρόπους για να εκτελέσει μια εργασία σε ένα φόντο, έναν τρόπο να πάει αν θέλετε να έχετε "πυρκαγιά και ξεχάσετε" προσέγγιση για την παράδοση κοχλιωτών εκτέλεση κομμάτια του κώδικα σας.

instagram viewer

AsyncCalls από τον Andreas Hausladen

Σημείωση: τα παρακάτω θα ήταν πιο εύκολο να ακολουθήσετε εάν πρώτα κατεβάσετε τον πηγαίο κώδικα.

Εξετάζοντας περισσότερους τρόπους για να εκτελέσετε μερικές από τις λειτουργίες μου με σπείρωμα, αποφάσισα να δοκιμάσω επίσης τη μονάδα "AsyncCalls.pas" που ανέπτυξε ο Andreas Hausladen. Ο Άντι AsyncCalls - Ασύγχρονες κλήσεις λειτουργίας μονάδα είναι μια άλλη βιβλιοθήκη που μπορεί να χρησιμοποιήσει ένας προγραμματιστής Delphi για να διευκολύνει τον πόνο της υλοποίησης της σπειροειδούς προσέγγισης για την εκτέλεση κάποιου κώδικα.

Από το blog του Andy: Με το AsyncCalls μπορείτε να εκτελέσετε πολλαπλές λειτουργίες ταυτόχρονα και να τις συγχρονίσετε σε κάθε σημείο της λειτουργίας ή της μεθόδου που τις ξεκίνησαν... Η μονάδα AsyncCalls προσφέρει μια ποικιλία πρωτοτύπων λειτουργίας για την κλήση ασύγχρονων λειτουργιών... Εφαρμόζει μια δεξαμενή νήματος! Η εγκατάσταση είναι εξαιρετικά εύκολη: απλά χρησιμοποιήστε asynccalls από οποιαδήποτε μονάδα σας και έχετε άμεση πρόσβαση σε πράγματα όπως "εκτελέστε σε ξεχωριστό νήμα, συγχρονίστε τον κύριο περιβάλλον χρήστη, περιμένετε μέχρι να τελειώσει".

Εκτός από την ελεύθερη χρήση (άδεια MPL) AsyncCalls, ο Andy επίσης δημοσιεύει συχνά τις δικές του ενημερώσεις κώδικα για τον Delphi IDE όπως "Επιτάχυνση των Δελφών" και "DDevExtensions"Είμαι βέβαιος ότι έχετε ακούσει (εάν δεν το χρησιμοποιείτε ήδη).

AsyncCalls In Action

Στην ουσία, όλες οι λειτουργίες AsyncCall επιστρέφουν μια διεπαφή IAsyncCall που επιτρέπει τον συγχρονισμό των λειτουργιών. Η IAsnycCall εκθέτει τις ακόλουθες μεθόδους:

 //v 2.98 από asynccalls.pas
IAsyncCall = διεπαφή
// περιμένει έως ότου ολοκληρωθεί η λειτουργία και επιστρέφει την τιμή επιστροφής
Συγχρονισμός συνάρτησης: Ακεραίο.
// Επιστρέφει True όταν τελειώσει η συνάρτηση asynchron
λειτουργία τερματισμένη: Boolean;
// επιστρέφει την τιμή επιστροφής της συνάρτησης ασύγχρονης, όταν είναι Τελική
συνάρτηση ReturnValue: ακέραιος αριθμός;
// λέει στο AsyncCalls ότι η εκχωρημένη λειτουργία δεν πρέπει να εκτελεστεί στο τρέχον threa
διαδικασία ForceDifferentThread;
τέλος;

Ακολουθεί ένα παράδειγμα κλήσης σε μια μέθοδο που περιμένει δύο ακέραιες παραμέτρους (επιστροφή ενός IAsyncCall):

 TAsyncCalls. Επικεφαλίστε (AsyncMethod, i, Random (500)).
λειτουργία TAsyncCallsForm. AsyncMethod (taskNr, sleepTime: ακέραιο): ακέραιο;
ξεκινήσει
αποτέλεσμα: = sleepTime;
Ύπνος (sleepTime);
TAsyncCalls. VCLInvoke (
διαδικασία
ξεκινήσει
Καταγραφή (Μορφή ('τελείωσε> αριθ:% d / εργασίες:% d / ύπνος:% d', [tasknr, asyncHelper. TaskCount, sleepTime])).
τέλος);
τέλος;

Τα TAsyncCalls. Το VCLInvoke είναι ένας τρόπος για να κάνετε συγχρονισμό με το κύριο νήμα σας (το κύριο νήμα της εφαρμογής - το περιβάλλον εργασίας χρήστη της εφαρμογής σας). Το VCLInvoke επιστρέφει αμέσως. Η ανώνυμη μέθοδος θα εκτελεστεί στο κύριο νήμα. Υπάρχει επίσης το VCLSync που επιστρέφει όταν ονομάστηκε ανώνυμη μέθοδος στο κύριο νήμα.

Πισίνα θεμάτων σε AsyncCalls

Πίσω στην εργασία "σάρωσης αρχείων": όταν τροφοδοτείται (σε ​​ένα βρόχο για) το asynccalls file pool με σειρά TAsyncCalls. Οι κλήσεις Invoke (), οι εργασίες θα προστεθούν στην εσωτερική πισίνα και θα εκτελεστούν "όταν έρθει η ώρα" (όταν έχουν τελειώσει οι προστιθέμενες κλήσεις).

Περιμένετε όλα τα IAsyncCalls να τελειώσουν

Η συνάρτηση AsyncMultiSync που ορίζεται στο asnyccalls περιμένει για να ολοκληρωθούν οι κλήσεις ασύγχρονης (και άλλες λαβές). Υπάρχουν μερικές υπερφορτωμένο τρόπους για να καλέσετε το AsyncMultiSync, και εδώ είναι το πιο απλό:

λειτουργία AsyncMultiSync (const Λίστα: σειρά από IAsyncCall; WaitAll: Boolean = True; Χιλιόμετρα: Καρδινάλιος = INFINITE): Καρδινάλιος. 

Αν θέλω να εφαρμόσω "wait all", πρέπει να συμπληρώσω μια σειρά IAsyncCall και να κάνω AsyncMultiSync σε φέτες των 61.

Βοηθός μου AsnycCalls

Εδώ είναι ένα κομμάτι του TAsyncCallsHelper:

ΠΡΟΕΙΔΟΠΟΙΗΣΗ: μερικός κώδικας! (ο πλήρης κώδικας είναι διαθέσιμος για λήψη)
χρήσεις AsyncCalls;
τύπος
TIAsyncCallArray = σειρά από IAsyncCall;
TIAsyncCallArrays = σειρά από TIAsyncCallArray;
TAsyncCallsHelper = τάξη
ιδιωτικός
fTasks: TIAsyncCallArrays;
ιδιοκτησία Εργασίες: TIAsyncCallArrays ανάγνωση fTasks;
δημόσιο
διαδικασία AddTask (const κλήση: IAsyncCall);
διαδικασία Περιμένετε;
τέλος;
ΠΡΟΕΙΔΟΠΟΙΗΣΗ: μερικός κώδικας!
διαδικασία TAsyncCallsHelper. Περιμένετε;
var
i: ακέραιο;
ξεκινήσει
Για i: = Υψηλή (Εργασίες) μέχρι Χαμηλή (Εργασίες) κάνω
ξεκινήσει
AsyncCalls. AsyncMultiSync (Εργασίες [i]);
τέλος;
τέλος;

Με αυτόν τον τρόπο μπορώ να "περιμένω όλα" σε κομμάτια των 61 (MAXIMUM_ASYNC_WAIT_OBJECTS) - δηλαδή να περιμένω συστοιχίες του IAsyncCall.

Με τα παραπάνω, ο βασικός μου κώδικας για την τροφοδοσία της πισίνας νήματος μοιάζει με:

διαδικασία TAsyncCallsForm.btnAddTasksClick (αποστολέας: TObject);
const
αριθμός = 200;
var
i: ακέραιο;
ξεκινήσει
asyncHelper. MaxThreads: = 2 * Σύστημα. CPUCount;
ClearLog ('εκκίνηση');
Για i: = 1 στους αριθμούς κάνω
ξεκινήσει
asyncHelper. ΠροσθήκηTask (TAsyncCalls. Καλέστε (AsyncMethod, i, Random (500))).
τέλος;
Καταγραφή ('all in');
// περιμένετε όλα
//asyncHelper.WaitAll;
// ή να επιτρέψετε την ακύρωση όλων που δεν ξεκίνησε κάνοντας κλικ στο πλήκτρο "Ακύρωση όλων":

ενώ ΔΕΝ asyncHelper. Ολα τελειωσαν κάνω Εφαρμογή. ProcessMessages;
Καταγραφή ('τελικό');
τέλος;

Ακύρωση όλων; - Πρέπει να αλλάξετε το AsyncCalls.pas :(

Θα ήθελα επίσης να έχω έναν τρόπο "ακύρωσης" των καθηκόντων που υπάρχουν στην πισίνα, αλλά περιμένουν την εκτέλεση τους.

Δυστυχώς, το AsyncCalls.pas δεν παρέχει έναν απλό τρόπο για την ακύρωση μιας εργασίας αφού έχει προστεθεί στην ομάδα νήματος. Δεν υπάρχει IAsyncCall. Ακύρωση ή IAsyncCall. DontDoIfNotAlreadyExecuting ή IAsyncCall. Μη μου δίνετε σημασία.

Για να λειτουργήσει αυτό, έπρεπε να αλλάξω το AsyncCalls.pas προσπαθώντας να το αλλάξω όσο το δυνατόν λιγότερο - έτσι ότι όταν ο Andy κυκλοφορήσει μια νέα έκδοση, θα πρέπει να προσθέσω μερικές μόνο γραμμές για να έχει την ιδέα μου "Άκυρο έργο" εργαζόμενος.

Ακολουθεί αυτό που έκανα: Έχω προσθέσει μια "διαδικασία Ακύρωση" στο IAsyncCall. Η διαδικασία ακύρωσης ορίζει το πεδίο "FCancelled" (προστιθέμενο) το οποίο ελέγχεται όταν η ομάδα πρόκειται να ξεκινήσει την εκτέλεση της εργασίας. Χρειάστηκα να αλλάξω λίγο το IAsyncCall. Ολοκληρώθηκε (έτσι ώστε οι αναφορές κλήσεων να τελειώνουν ακόμα και όταν ακυρώνονται) και το TAsyncCall. InternExecuteAsyncCall διαδικασία (να μην εκτελέσει την κλήση αν έχει ακυρωθεί).

Μπορείς να χρησιμοποιήσεις WinMerge για να εντοπίσετε εύκολα τις διαφορές μεταξύ του αρχικού asynccall.pas του Andy και της τροποποιημένης έκδοσης (που περιλαμβάνεται στη λήψη).

Μπορείτε να κατεβάσετε τον πλήρη πηγαίο κώδικα και να εξερευνήσετε.

Ομολογία

ΕΙΔΟΠΟΙΗΣΗ! :)

ο CancelInvocation η μέθοδος διακόπτει την κλήση του AsyncCall. Εάν η AsyncCall έχει ήδη υποβληθεί σε επεξεργασία, μια κλήση στην CancelInvocation δεν έχει καμία επίδραση και η λειτουργία Canceled (Άκυρο) θα επιστρέψει ψευδές, καθώς η AsyncCall δεν ακυρώθηκε.
ο Ακυρώθηκε η μέθοδος επιστρέφει True εάν η AsyncCall ακυρώθηκε από το CancelInvocation.
ο Ξεχνάμε αποσυνδέει τη διασύνδεση IAsyncCall από την εσωτερική συσκευή AsyncCall. Αυτό σημαίνει ότι αν η τελευταία αναφορά στη διασύνδεση IAsyncCall έχει χαθεί, η ασύγχρονη κλήση θα εκτελεστεί ακόμα. Οι μέθοδοι της διασύνδεσης θα ρίξουν μια εξαίρεση εάν καλούνται μετά την κλήση του Forget. Η συνάρτηση async δεν πρέπει να καλεί στο κύριο νήμα επειδή θα μπορούσε να εκτελεστεί μετά το TThread. Ο μηχανισμός συγχρονισμού / ουράς τερμάτισε από το RTL αυτό που μπορεί να προκαλέσει μια κλειστή ασφάλιση.

Σημειώστε, ωστόσο, ότι μπορείτε ακόμα να επωφεληθείτε από το AsyncCallsHelper μου εάν πρέπει να περιμένετε όλες τις κλήσεις async να τελειώσουν με το "asyncHelper. WaitAll "; ή αν θέλετε να "Ακύρωση όλων".

instagram story viewer