Αν είχες ρωτήσει κατευθείαν "Θέλω να επεξεργαστώ πολλά εισερχόμενα e-mail" θα σου έλεγα απευθείας να χρησιμοποιήσεις ένα ActionBlock από το TPL Dataflow library. Δεν υπάρχουν πολλαπλά message loops σε μία Windows εφαρμογή, ούτε χρειάζονται για να επεξεργαστείς κάτι ασύγχρονα. Ακριβώς επειδή υπάρχει μόνο ένα Message loop, δεν μπορείς να πειράξεις και ένα UI control από διαφορετικά threads.
Αντί γι αυτό, καλύτερα να χρησιμοποιήσεις ένα ActionBlock<T> το οποίο θα καλεί ένα function για να επεξεργαστεί κάθε μήνυμα. To ActionBlock κάνει buffer τα εισερχόμενα μηνύματα αν και μπορείς να ορίσεις όριο, για να μην μεγαλώσει υπερβολικά ο buffer αν είναι αργό το function. Επίσης μπορείς να ορίσεις ότι θα χρησιμοποιηθούν παραπάνω από ένα tasks για την επεξεργασία των μηνυμάτων, οπότε η επεξεργασία θα γίνει παράλληλα.
Ο κώδικας είναι πολύ απλός:
var myBlock = new ActionBlock<MyEmail>(it => MyProcessingMethod(it) );
foreach (var email in myListOfEmails)
{
myBlock.Post(email);
}
Όταν τελειώσει η επεξεργασία και θέλεις να κλείσεις το block, καλείς την Complete() και περιμένεις να τελειώσει το Completion task, το οποίο σημαίνει ότι καθάρισαν οι εκκρεμότητες:
myBlock.Complete();
await myBlock.Completion;
Το TPL Dataflow περιέχει και άλλα χρήσιμα blocks. Για παράδειγμα, το TransformBlock καλεί ένα function το οποίο λαμβάνει ένα input και επιστρέφει ένα διαφορετικό output. Αυτό σου επιτρέπει να σπάσεις τη διαδικασία της επεξεργασίας σε βήματα, όπως θα έκανες και στο command line και να περάσεις τα αποτελέσματα του ενός βήματος, στο επόμενο. Κάθε βήμα εκτελείται με διαφορετικά tasks, οπότε έχεις άλλο ένα τρόπο να πετύχεις ασύγχρονη επεξεργασία. Πχ.
var myParser=new TransformBlock<MyEmail,MyCustomer>(x=>GetCustomerFromEmail(x));
var myBlock = new ActionBlock<MyCustomer>(x=> DoSomethingWithCustomer(x));
myParser.LinkTo(myBlock, new DataflowLinkOptions { PropagateCompletion = true });
....
myParser.Post( email);
...
myParser.Complete();
await myBlock.Completion;
Εδώ το LinkTo μεταφέρει το αποτέλεσμα από το πρώτο βήμα στο επόμενο. Το PropagateCompletion σημαίνει ότι αν ολοκληρωθεί το προηγούμενο βήμα, θα πρέπει να ολοκληρωθεί και το επόμενο. Μόλις τελειώσουν τα μηνύματα καλείται η Complete() στο πρώτο βήμα και περιμένουμε μέχρι να τελειώσει και το τελευταίο μήνυμα
Παναγιώτης Καναβός, Freelancer
Twitter: http://www.twitter.com/pkanavos