Δευτέρα 5 Σεπτεμβρίου 2011

Windows GUI και asynchronism

Σε αυτό εδώ το άρθρο/ανησυχίες μου (πείτε το όπως θέλετε) θα γράψω που πρέπει να  έχουμε ασυγχρόνιστες εργασίες και που όχι.
Ασυγχρόνιστες εργασίες


Όταν λέμε ασυγχρόνιστες εργασίες εννοούμε οτι θα εκτελέσουμε κάποια πράγματα ταυτόχρονα. Για να το κάνουμε αυτό μπορούμε να χρησιμοποιήσουμε Threads,Tasks,Parallels και async calls, όλα αυτά βασίζονται στα threads ή νήματα. Νομίζω πως το "νήμα" είναι η καλύτερη μεταφορά αγγλικού όρου στα ελληνικά.
     Για να καταλάβουμε πως διαχειρίζονται τα νήματα από το λειτουργικό μπορούμε να παρομοιάσουμε όλη την διαδικασία σαν το πλέξιμο μίας κάλτσας. Δηλαδή θέλουμε μία μαύρη κάλτσα με μπλε και άσπρες ρίγες, αυτό ταυτίζεται με το αποτέλεσμα της επεξεργασίας, άρα θέλουμε τα δεδομένα μας που είναι οι κλωστές (νήματα), θέλουμε και μια γιαγιά που θα είναι το λειτουργικό, και τις πλεξίδες (αυτά τα σιδεράκια, δεν ξερό την ονομασία τους) που θα παίζουν το ρολό του CPU. Αυτό που θα κάνει το λειτουργικό μας με τα 3 νήματα ταυτίζεται με αυτό που θα κάνει η γιαγιά μας. Δηλαδή, θα πάρει το μαύρο νήμα, θα πλέξει ένα κομμάτι, θα αφήσει το μαύρο νήμα και θα πάρει το άσπρο, θα πλέξει ένα κομμάτι, θα αφήσει το άσπρο νήμα θα πάρει το μπλε θα πλέξει ένα κομμάτι, θα αφήσει το μπλε νήμα θα πάρει το μαύρο και φτου από την αρχή μέχρι να τελειώσουν τα νήματα.

     Όπως βλέπετε και στην πάνω εικόνα, είτε έχουμε ένα νήμα είτε πολλά, ο χρόνος εκτέλεσης θα είναι σχετικά ίδιος. Τότε γιατί θέλουμε νήματα; Η απάντηση είναι απλή, μία εργασία δεν εξαρτάται μονό από το CPU.


 Γιατί θέλουμε νήματα σε ένα GUI των windows

    Ένα window είναι ένα process και ένα νήμα. Είπαμε τι είναι ένα νήμα, αλλά process; Τι είναι αυτό; Το process δεν είναι τίποτα περισσότερο από έναν container. Τα χαρακτηριστικά του:
  1. Είναι 4 GB
  2. Είναι χωρισμένος σε segments (bss,heap,stack,data)
  3. Οι δείκτες δεν δείχνουν ποτέ σε μνήμη άλλης process
Λοιπόν όταν φτιάχνουμε ένα windows τότε έχουμε ένα process και ένα νήμα. Στο process πάνε τα resources,functions και οτιδήποτε έχει να κάνει με μνήμη. Στο νήμα τρέχει ο message dispacher σειριακά με την WndProc


     Αυτό είναι το runtime ενός windows GetMessage -> TranslateMessage -> DispachMessage -> Work With Message -> και πάλι από την αρχή. Και όλο αυτό είναι σε ένα νήμα.

Αυτό μας φέρνει δυο προβλήματα όταν η wndproc κολλάει:

Not Responding
     Αυτό το πρόβλημα το έχετε δει αρκετές φορές. Το πρόβλημα αυτό δημιουργείται όταν το message pool / ( για είμαι ακριβής λέγετε message queue) δεν λαμβάνει επιβεβαίωση αποστολής. "Επιβεβαίωση αποστολής" ισούται "η WndProc έχει κολλήσει". Οι λόγοι για το κόλλημα της WndProc είναι πολλοί, πχ το άνοιγμα ενός αρχείου.
Εδώ ένα NotResponding πρόγραμμα (πατά οκ και προσπάθησε να κουνήσεις το παράθυρο)

Αδυναμία ενημέρωσης των controls
     Αυτό το πρόβλημα δημιουργείται όταν η εργασία μας είναι σε σταδία και θέλουμε να μας ενημερώνει ανα στάδιο. Πχ έχουμε 3 συναρτήσεις(OpenWork Work CloseWork) στο OK button click event και θέλουμε ενημέρωση ανά κάθε στάδιο ένα static control (label)
OpenWork 
SetLabelText(WorkIsOpen) 
Work
SetLabelText(Working...)
CloseWork
SetLabelText(Done)

     Το πρόβλημα, στέλνουμε msg για την ενημέρωση του static αλλά είμαστε ακόμα στη WndProc! Έτσι το πρόγραμμα μας είναι σε Not Responding state, και το msg περιμένει στο message queue. Το ίδιο και τα αλλά δυο msg, και όταν η WndProc τελειώσει τότε θα λάβει αυτά τα 3 msg μαζί με αποτέλεσμα το static να έχει την τιμή Done.

     Όποιος ασχολείται με τον προγραμματισμό πρέπει οπωσδήποτε να ασχοληθεί με νήματα ή οτιδήποτε του δίνει την δυνατότητα του asynchronism. Δε λέω να φτιάξεις τι σούπερ δούπερ εφαρμογή, αλλά ρε παιδί μου να μην πετάει και Not Responding στον χρηστή... ειναι αμαρτία.

Multithreading με σκοπό το καλύτερο performance

     Με το multithreading μπορούμε να πετύχουμε πολύ καλές επιδώσεις για μια εργασία, θα μου πεις τώρα πως γίνεται να λέω στην αρχή οτι είτε έχουμε ένα νήμα είτε πολλά η εργασία θα τελειώσει στον ίδιο χρόνο. Είναι απλό, στην αρχή αναφέρομαι μονό για εργασία σε επίπεδο CPU (έχω βάλει και την ανάλογη εικόνα). Το θέμα είναι οτι πολλές εργασίες δεν μπορούν να γίνουν μονό στο cpu αλλά θα θέλουν και τη ram τους ή και κάνα αρχείο ή οποιοδήποτε IO call που μπορεί να είναι ένα πληκτρολόγιο ή ένα ποντίκι. Εκεί είναι που μπορούμε να κερδίσουμε χρόνο.

     Λοιπόν θα φτιάξω 3 πρόγραμμα τα οποία θα βγάζουν τα md5 checksums αρκετών αρχείων. Αυτό που θα προσπαθήσω να κανό είναι:
Εφαρμογή #1 medium performance
Εφαρμογή #2 normal performance
Εφαρμογή #3 high performance  

Και οι 3 εφαρμογές θα έχουν το ίδιο interface το οποίο είναι.




Το interface θα είναι αρκετά απλό, παίρνουμε τα άρχει που θέλουμε από τον φάκελο και τα πετάμε στην εφαρμογή μετα πατάμε οκ και viola.

Εφαρμογή #1

      Σε αυτή τη περίπτωση θα χρησιμοποιήσω 2 thread ένα για το GUI και το άλλο για worker. Θα φτιάξω ένα object MD5Provider το οποίο είναι ένας wrapper του wincrypt api και άλλο ένα Timer το οποίο είναι άλλος ένας wrapper για το QueryPerformanceCount api ή οπός λέγεται. 
     Η διαδικασία θα είναι απλή, θα πάρω τα checksums σειριακά, θα αρχίσω με το πρώτο αρχείο και όταν τελειώσω θα πάω στο επόμενο κλπ κλπ.



Εφαρμογή #2

     Σε αυτή τη περίπτωση θα εκμεταλλευτώ την διαφορά ταχύτητας ανάμεσα στους σκληρούς και τον controller. Ο σκληρός ειναι αργός ο controller είναι γρήγορος. Ο δεύτερος είναι τόσο γρήγορος που μπορώ να διαβάζω με την ίδια ταχύτητα δυο σκληρούς ταυτόχρονα. Με αυτή την λογική το μονό που πρέπει να κάνω είναι 
Να δω τι φυσικές συσκευές έχω
Να χωρίσω τα αρχεία σχετικά με το σε πιο σκληρό βρίσκονται
Τέλος να ανοίξω Ν(σκληρούς) νήματα 
Στη περίπτωση που τσεκάρω 2 gb από το C:\ το αποτέλεσμα είναι 30 δευτερόλεπτα, εάν όμως τσεκάρω 1 gb από το C:\ και άλλο ένα gb από το E:\ το αποτέλεσμα ειναι 15 δευτερόλεπτα 



Δεν υπάρχουν σχόλια:

Δημοσίευση σχολίου