Παρασκευή, 19 Φεβρουαρίου 2016

Εγχειρίδιο της Μ2000 - Τεύχος 12ο

6. Εκτός Βασικής Ροής Εκτέλεση Εντολών (Παράλληλη Ροή)

Είδαμε στο 5ο κεφάλαιο (Τεύχη 9,10,11) ζητήματα που έχουν να κάνουν με τη ροή προγράμματος, που κάλυπταν την ιδέα ότι οι εντολές εκτελούνται η μία μετά την άλλη, και εν τέλει αν κάποιος μπορούσε να τις περιγράψει σε μια γραμμή με κουκκίδες και ονόματα, τότε θα είχαμε αυτό που  θα λέμε εδώ γραμμική εξέλιξη γεγονότων (απλός ορισμός, γεγονότα που διαδέχονται γεγονότα).

Σαν άλλη παράσταση θα ήταν να βάζαμε κουτάκια διαδοχικά και να γράφαμε σε ποιο σημείο του προγράμματος βρισκόμαστε. Αυτά τα κουτάκια δεν είναι το πρόγραμμα στην μορφή του κώδικα αλλά στην μορφή της εκτέλεσης. Αν δηλαδή είχαμε μια επανάληψη 100 φορές το Α++ (αύξηση κατά ένα) τότε θα είχαμε εκατό κουτάκια (ενώ στο κώδικα θα είχαμε τρεις γραμμές ή μια γραμμή με τρεις εντολές : Για ι=1 έως 100 : Α++ : Επόμενο ι)

Γίνεται φανερό ότι ένα κουτάκι Β (με την εντολή που εκτελείται) μετά το Α είναι συνέπεια του Α. Αυτή η ροή λέγεται σύγχρονη. Τι άλλο θα μπορούσε να γίνει;
Να υπάρχουν κουτάκια που παράλληλα εκτελούνται και κάποια να χειρίζονται άλλα αν θα συνεχίσουν ή όχι να εκτελούνται. Δηλαδή το κουτάκι Α δεν "παράγει" μόνο το Β αλλά εκκινεί και άλλα Β1, ή Δ1 (δηλαδή διαφορετικές εκδοχές). Η γραμμή εκτέλεσης δηλαδή ανοίγει σε πολλές γραμμές και αρχίζει η κάθε γραμμή να έχει τη δική της ιστορία! Αυτό λέγεται παράλληλη ροή και στην Μ2000 υλοποιείται με δυο τρόπους. με Νήματα για εκτέλεση κώδικα εντός προγράμματος ή με Αυλούς για επικοινωνία με άλλα προγράμματα σε διαφορετικά περιβάλλοντα. Η τελευταία περίπτωση μας δίνει τη δυνατότητα να φτιάξουμε ένα απλό σύστημα πελάτη-εξυπηρετητή. Θα τις διαχωρίζουμε ως Ασύγχρονη Λειτουργία Εκτέλεσης και ως Παράλληλη Εκτέλεση με Αυλούς.

6.1 Ασύγχρονη Λειτουργία Εκτέλεσης

Ένας τρόπος παράλληλης ροής είναι αυτός της ασύγχρονης λειτουργίας εκτέλεσης. Σε αυτή την περίπτωση έχουμε πολλές γραμμές εκτέλεσης, και υπάρχει δυνατότητα να περιμένει μια γραμμή για να συνεχίσει, ή να πάρει μήνυμα για να περιμένει μέχρι να πάρει μήνυμα για να συνεχίσει.

Στο κώδικα που γράφουμε δεν έχουμε την μορφή της εκτέλεσης σε απλή θέα. Δεν έχουμε τα κουτάκια που έχω αναφέρει πιο πριν, σχηματικά. Αυτό που έχουμε είναι κώδικας όπως ο κώδικας που γράφουμε σε ένα μπλοκ, με την έννοια όμως ότι ο κώδικας είναι τμήμα μιας επανάληψης. Αυτό συμβαίνει γιατί κύρια ο κώδικας της ασύγχρονης λειτουργίας χρησιμοποιείται κάνοντας έλεγχο σε κάτι κατά τακτά χρονικά διαστήματα. Ενώ δηλαδή στην σύγχρονη λειτουργία η καθυστέρηση υλοποιείται με την εντολή Αναμονή, στην ασύγχρονη ο κώδικας εκτελείται σε "τακτά" χρονικά διαστήματα Τ, άρα στο χρονικό διάστημα Τ θα είναι μέσα το Τ1 ο χρόνος εκτέλεσης και το Τ-Τ1 ο χρόνος αδράνειας.

6.1. Νήματα

Με απλά λόγια η ιδέα του κώδικα που εκτελείται σε Νήματα (θα τα ονομάζουμε νήματα, δηλαδή κλωστές ή γραμμές εκτέλεσης, όπως στη περιγραφή πιο πριν), περιλαμβάνει το διάστημα εκτέλεσης Τ και ενδέχεται να έχουμε χρόνο αδράνειας ή να έχουμε υπερχείλιση, δηλαδή ο χρόνος Τ να μεγαλώσει για να φέρει το χρόνο αδράνειας στο 0 (ουσιαστικά αυτό δεν μας κάνει, αλλά ο διερμηνευτής το αντιμετωπίζει).

6.1.2 Σχέδια Νημάτων 

Στην Μ2000 υπάρχουν δυο σχέδια νημάτων να επιλέξουμε.Το διαδοχικό και το ταυτόχρονο. Το διαδοχικό (Sequential) μας λέει ότι ο χρόνος αδράνειας θα είναι ο χρόνος που θα δίνεται σε άλλα νήματα. Το διαδοχικό λέει ότι κάθε εντολή εκτός αυτών που είναι σε μπλοκ (ή είναι κλήσεις σε τμήματα) εκτελείται και επιστρέφει τη εκτέλεση σε άλλο νήμα, άρα πριν φθάσουμε στο χρόνο αδράνειας.

Μπορούμε να επιλέξουμε σχέδιο νημάτων πριν ξεκινήσουμε νήματα. Οι εντολές είναι (τις εμφανίζω και στα αγγλικά εδώ - Η Μ2000 έχει δυο σύνολα εντολών Ελληνικές και Αγγλικές)
Σχέδιο.Νημάτων Διαδοχικό
Σχέδιο.Νημάτων Ταυτόχρονο
Thread.Plan Concurrent
Thread.Plan Sequential
Εξ ορισμού το σχέδιο είναι το Διαδοχικό


6.1.3 Ειδικά Νήματα

Πριν επεκταθούμε στα Νήματα, θα δούμε δυο ειδικά νήματα. Και τα δύο είδη είναι "αυτόματα" νήματα (θα το δούμε μετά, γιατί λέγονται αυτόματα)

6.1.3.1 Μετά

Μετά 500 {
      Τύπωσε "Οκ2"
}
Μετά 200 {
      Τύπωσε "Οκ1"
}
Αναμονή 1000
Τύπωσε "Εντάξει"

Το Οκ1 θα εμφανιστεί πριν το Οκ2 και μετά θα εμφανιστεί το Εντάξει. Αν αφαιρέσουμε το Αναμονή 1000 τότε δεν θα τρέξει κανένα νήμα (κάθε νήμα χρειάζεται χρόνο αδράνειας και εδώ το προσφέρει η Αναμονή)..Εξ ορισμού ένα τμήμα όταν τελειώσει με οποιονδήποτε τρόπο (ακόμα και με το πλήκτρο  Esc) τερματίζει τα νήματα που έχει δημιουργήσει, ανεξάρτητα αν έτρεξαν.

6.1.3.2 Κύριο Έργο

Δείτε τώρα στο παρακάτω πρόγραμμα το Κύριο.Έργο. Είναι  ένα νήμα που λέει κάθε πότε θα τρέχει αλλά έχει μια ιδιότητα. Τρέχει μόνο αν υπάρχουν άλλα νήματα σε αναμονή - ή τρέχουν, Αν δεν υπάρχουν τερματίζει! Αν αφαιρέσουμε τις φωλιασμένες Μετά δεν θα έχουμε εκτέλεση της Κύριο.Έργο. Επιπλέον δεν μπορούμε να βάλουμε φωλιασμένα νήματα τύπου Κύριο.Έργο.

Μετά 100 {
      Μετά 200 {
            Τύπωσε "Τέλος"
      }
}
Α=0
Κύριο.Έργο 50 {
       Τύπωσε Α
       Α++
}

Μόλις παίξει η δεύτερη μετά και εμφανίσει το "Τέλος" η Κύριο.Έργο δεν έχει άλλο έργο να κάνει και τερματίζει και αυτή. Μπορούμε παρακάτω να έχουμε μια άλλη Κύριο.Έργο, Ή μπορούμε για κάποιο λόγο να τερματίσουμε την Κύριο.Έργο με την εντολή Έξοδος ή Διέκοψε. Αλλά απαιτείται μια εντολή Νήματα Σβήσε για να είμαστε σίγουροι ότι θα σβήσουν όλα τα νήματα (ενδέχεται ένα νήμα να προλάβει να τρέξει κατά την επιστροφή του τμήματος αν δεν τα σβήσουμε)

Μετά 100 {
      Μετά 200 {
            Τύπωσε "Τέλος"
      }
}
Α=0
Κύριο.Έργο 50 {
       Τύπωσε Α
       Α++
       Αν Α>3 τότε Διέκοψε
}
Νήματα Σβήσε
Τύπωσε "Τέλος Έργου"

6.1.4 Απλά Νήματα

Τα απλά νήματα καθορίζονται με ένα αριθμό, ή όπως λέμε με έναν χειριστή, δηλαδή μια μεταβλητή που μας δίνει τον αριθμό νήματος. Εδώ δηλαδή κατανοούμε ότι τα νήματα δεν έχουν ονόματα. Αυτό μας βολεύει αν θέλουμε έναν ορισμό να τον τρέξουμε με επανάληψη και έτσι να δημιουργήσουμε πολλαπλά νήματα, με ίδιο κώδικα, αλλά άλλο αριθμό. Οι αριθμοί δίνονται με μια σειρά, και δεν είναι άνευ ορίου, κάνουν όμως κύκλο. Προγραμματιστικά τα ενεργά νήματα περιορίζονται σε δέκα χιλιάδες, στην πράξη αυτός ο αριθμός είναι υπερβολικός (για την Μ2000, υπό την παρούσα υλοποίηση).

Α=10
Νήμα {
      Α++
} ως Β Κάθε 10


Κάθε 30 {
      Τυπωσε Α
      Αν Α>100 τότε έξοδος
}
Νήμα Α Σβήσε



  • Αν αφαιρέσουμε το Κάθε 10 μετά το Ως Β τότε το νήμα δεν θα ξεκινήσει.
  • Μπορούμε κάποια άλλη στιγμή να γράψουμε  Νήμα Α Κάθε 10 οπότε έτσι ξεκινάει.
  • Μπορούμε μέσα σε ένα νήμα να γράψουμε Νήμα Αυτό Κάθε 100, δηλαδή να στείλει το νήμα στον εαυτό του το μήνυμα να αλλάξει χρόνο επανάληψης εκτέλεσης.
  • Οι Α και Β είναι μεταβλητές του τμήματος. Τα νήματα βλέπουν τις μεταβλητές του νήματος, εκτός από τις στατικές τους μεταβλητές διότι τα νήματα έχουν δικές τους στατικές μεταβλητές!
  • Η Κάθε  {} δεν είναι νήμα. μοιάζει με την Κύριο.Έργο {} αλλά επειδή δεν είναι νήμα δεν έχει την ιδιότητα της Κύριο.Έργο να ισορροπεί τους χρόνους εκτέλεσης (όταν ό,τι τρέχει είναι νήμα τότε έχουμε την καλύτερη διαχείριση χρόνου. Η Κάθε { } έχει μια Αναμονή εσωτερικά

Α=10
Νήμα {
      Α++
} ως Β Κάθε 10


Κύριο.Έργο 30 {
      Τυπωσε Α
      Αν Α>100 τότε έξοδος
}
\\ εμφανίζει το ΝΗΜΑΤΑ ΣΕ ΕΠΕΞΕΡΓΑΣΙΑ:0
Νήματα
Νήματα Σβήσε \\ για σιγουριά! Αν και εδώ θα σβήσει στη Κύριο.Έργο


6.1.5 Εκτέλεση εντολών σε νήμα εκτός ροής!

Δημιουργούμε πέντε νήματα, και σε διαιρούμε ένα χρονικό διάστημα με δυο εντολές Αναμονή, και στο ενδιάμεσο εκτελούμε σε δυο νήματα εντολές (που στην ουσία είναι καταχώρηση τιμής στην δική τους στατική. Τα νήματα αν και διαβάζουν τις μεταβλητές από το τμήμα, τρέχουν σε δικό τους αντικείμενο εκτέλεσης, με δικές τους στατικές, με δικό τους σωρό τιμών και όταν καλούν ένα τμήμα όπως εδώ το Κοίτα, δημιουργούν διαφορετικό "πακέτο" στατικών για την Κοίτα. Δηλαδή το τμήμα Κοίτα έχει πέντε πακέτα στατικών Λ και Β επειδή έχει πέντε κλήσεις από πέντε διαφορετικά αντικείμενα εκτέλεσης. Η β στο τμήμα (όχι στην Κοίτα) είναι κανονική μεταβλητή η οποία έχει τον αριθμό του πέμπτου νήματος. Οπότε στέλνουμε εντολές σε αυτό και στο β-3, ή 5-3 άρα το δεύτερο νήμα.
Αυτό που κάνουμε δηλαδή είναι να χειριστούμε το νήμα από αλλού (εδώ από το τμήμα, σε άλλη περίπτωση από άλλο νήμα). Δηλαδή όχι μόνο έχουμε νήματα που εκτελούνται εκτός βασικής ροής αλλά και σε αυτά μπορούμε να επέμβουμε και να τροποποιήσουμε μεταβλητές (και όπως έχω γράψει αλλού, βλέπουμε τις μεταβλητές ως μηνύματα, και η τροποποίηση σημαίνει αλλαγή μηνύματος).

Σχέδιο.Νημάτων Ταυτόχρονο
Τμήμα Κοίτα {
      Στατική λ=Αριθμός, β=Αριθμός
      λ++
      Τύπωσε Μορφή$( "Κοίτα {0} από {1}", λ, β)
}
Για ι=1 έως 5
      Νήμα {
            Νήμα Θέσε μια_φορά=Αληθές
            \\ μέσα σε μπλοκ για να εκτελεστεί μια φορά
            {Αν μια_φορά Τότε { Κοίτα κλμ, ββ : μια_φορά=Ψευδές } Αλλιώς Κοίτα }
            Τύπωσε μ, ββ, β$, κλμ
            μ++
      } Ως β Εκτέλεση Νήμα Θέσε μ=10**ι, ββ=β, β$="Γιώργο", κλμ=Τυχαίος(1000)
      Νήμα β Κάθε 100
Επόμενο ι
Αναμονή 2000
Νήμα β Εκτέλεση β$ = "Γειά σου " +β$
Νήμα β-3 Εκτέλεση β$ = "Γεια Χαρά " +β$
Αναμονή 2000
Λίστα   'μόνο οι ι και β είναι μεταβλητές του τμήματος
Νήματα Σβήσε



(Σε επόμενο τεύχος θα δούμε και άλλες εντολές νημάτων)
(Σε παράδειγμα επίσης σε άλλο τεύχος θα δούμε τα νήματα να μπορούν να διαβάζουν με εισαγωγή έναν αριθμό ή ένα αλφαριθμητικό χωρίς να διακόπτουν άλλα νήματα!)


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

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