Παραδείγματα εργαστηρίου (2019-2020)
Ημερολόγιο μαθημάτων
- 21/02/2020 (Παρασκευή 13:00-15:00) {Τμήμα 1 + Τμήμα 2}
- 26/02/2020 (Τετάρτη 12:00-14:00) {Τμήμα 1}
- 06/03/2020 (Παρασκευή 13:00-15:00) {Τμήμα 2}
- 13/03/2020 (Παρασκευή 13:00-15:00) {Τμήμα 1}
- 27/03/2020 (Παρασκευή 13:00-15:00) {MSTEAMS}
- 03/04/2020 (Παρασκευή 13:00-15:00) {MSTEAMS}
- 10/04/2020 (Παρασκευή 13:00-15:00) {MSTEAMS}
- 24/04/2020 (Παρασκευή 13:00-15:00) {MSTEAMS}
- 08/05/2020 (Παρασκευή 13:00-15:00) {MSTEAMS}
ΕΡΓΑΣΤΗΡΙΟ 1 & 2
Σειριακός κώδικας που υπολογίζει το άθροισμα ακεραίων από το 1 μέχρι και το Ν (Ν=10000).
-
$ gcc lc00_serial.c -o lc00_serial $ ./lc00_serial
Παράδειγμα με POSIX Processes. Υπολογισμός αθροίσματος ακεραίων τιμών από το 1 μέχρι και το Ν (Ν=10000) χρησιμοποιώντας 2 διεργασίες. Η διεργασία παιδί υπολογίζει το άθροισμα 1 + 2 + … + Ν/2 ενώ η διεργασία γονέας υπολογίζει το άθροισμα (Ν/2 + 1) + (Ν/2 + 2) + … + Ν.
-
$ gcc lc01_processes.c -o lc01_processes $ ./lc01_processes
Παράδειγμα με pThreads. Υπολογισμός αθροίσματος ακεραίων τιμών από το 1 μέχρι και το Ν (Ν=10000) χρησιμοποιώντας Τ νήματα (Τ=2). Το ένα νήμα υπολογίζει το άθροισμα 1 + 2 + … + Ν/2 ενώ το άλλο νήμα υπολογίζει το άθροισμα Ν/2+1 + Ν/2+2 + … + Ν. Το κύριο νήμα αναλαμβάνει την πρόσθεση των αθροισμάτων που υπολογίζουν τα νήματα.
-
lc02_pthreads.c Απλοϊκή έκδοση, ξεχωριστή συνάρτηση για κάθε νήμα.
$ gcc lc02_pthreads.c -o lc02_pthreads -lpthread $ ./lc02_pthreads
-
lc02_pthreads1.c Έκδοση που λειτουργεί και με περισσότερα από 2 threads.
$ gcc lc02_pthreads1.c -o lc02_pthreads1 -lpthread $ ./lc02_pthreads1
-
lc02_pthreads2.c Έκδοση που λειτουργεί και με περισσότερα από 2 threads (αλλαγή στον τρόπο με τον οποίο περνά το αναγνωριστικό νήματος στη συνάρτηση που θα εκτελέσει το νήμα).
$ gcc lc02_pthreads2.c -o lc02_pthreads2 -lpthread $ ./lc02_pthreads2
-
lc02_pthreads3.c Έκδοση του κώδικα που δέχεται τον αριθμό νημάτων ως παράμετρο γραμμής εντολών.
$ gcc lc02_pthreads3.c -o lc02_pthreads3 -lpthread $ ./lc02_pthreads3 10
-
lc02_pthreads4.c Έκδοση του κώδικα που δέχεται τον αριθμό νημάτων ως παράμετρο γραμμής εντολών και χρησιμοποιεί mutex για συγχρονισμό των νημάτων.
$ gcc lc02_pthreads4.c -o lc02_pthreads4 -lpthread $ ./lc02_pthreads4 10
ΕΡΓΑΣΤΗΡΙΟ 3 (κρίσιμες περιοχές)
Κώδικας υπολογισμού y = A * x (δίνεται ο δισδιάστατος πίνακας Α και το διάνυσμα x)
Κώδικες που επιδεικνύουν την αναγκαιότητα συγχρονισμού στον παράλληλο και ταυτόχρονο προγραμματισμό.
Ένα σύνολο νημάτων πραγματοποιούν τον ίδιο αριθμό μοναδιαίων αυξήσεων και μοναδιαίων μειώσεων στην κοινόχρηστη μεταβλητή counter που αρχικοποιείται στην τιμή 0. Η τελική τιμή της μεταβλητής counter θα έπρεπε να είναι 0 αλλά λόγω “συνθήκης ανταγωνισμού” δεν λαμβάνεται το σωστό αποτέλεσμα.
Επίλυση του προβλήματος με busy_wait (για 4 νήματα).
Το busy wait είναι επικίνδυνο αν πρόκειται να ζητηθεί βελτιστοποίηση κατά τη μεταγλώττιση. Επίλυση του προβλήματος με χρήση volatile μεταβλητών.
Επίλυση του προβλήματος με αμοιβαίο αποκλεισμό (mutex).
Επίλυση του προβλήματος με σημαφόρους.
ΕΡΓΑΣΤΗΡΙΟ 4 (φράγματα - μεταβλητές συνθήκης)
Χρησιμότητες των φραγμάτων
- Αναγκαιότητα εφαρμογής (υπολογισμοί Φάσης Α που πρέπει να ολοκληρωθεί από κάποια threads πριν τα νήματα προχωρήσουν σε μια δεύτερη φάση υπολογισμών)
- Χρονομέτρηση κώδικα
- Διευκόλυνση αποσφαλμάτωσης
Ένα απλό παράδειγμα με φράγμα
10 νήματα “κοιμούνται” για κάποια δευτερόλεπτα το καθένα εμφανίζοντας μήνυμα για το χρονικό διάστημα του “ύπνου τους”. Μόνο όταν όλα τα νήματα “ξυπνήσουν”, τότε το καθένα θα πρέπει να εμφανίσει μήνυμα για το ότι “ξύπνησε”.
- Απόπειρα λύσης χωρίς φράγμα.
- Λύση με φράγμα pthread_barrier_t.
- Λύση με custom υλοποίηση φράγματος καθώς ορισμένες υλοποιήσεις pthreads δεν έχουν υλοποίηση των φραγμάτων.
Ένα δεύτερο παράδειγμα χρησιμότητας των φραγμάτων
Έστω ότι 10 νήματα πρέπει πρώτα να προσθέσουν από μια μονάδα σε μια κοινόχρηστη μεταβλητή και μετά το καθένα να διπλασιάσει τη μεταβλητή.
- Κώδικας χωρίς barrier (λάθος αποτελέσματα)
- Χρήση ενσωματωμένου φράγματος (εφόσον το υποστηρίζει ο μεταγλωττιστής)
- Custom υλοποίηση φράγματος με mutex και αναμονή σε εκρήγορση
- Custom υλοποίηση φράγματος με conditional variable
Παράδειγμα με conditional variable
Σενάριο προβλήματος: Τρία νήματα Τ1, Τ2 και Τ3. Το νήμα Τ1 θα πρέπει να περιμένει να ξεκινήσουν τα νήματα Τ2 και Τ3 έτσι ώστε να ξεκινήσει. Τα νήματα Τ2 και Τ3 θα πρέπει να περιμένουν το Τ1 να τα ειδοποιήσει ότι μπορούν να συνεχίσουν την εκτέλεσή τους.
$ gcc lc04_conditional_var.c - o lc04_conditional_var -lpthread
$ lc04_conditional_var
T3:start
T3:wait T1 to finish
T2:start
T2:wait T1 to finish
T1:start
T1:work
T1:broadcast
T1:finish
T2:work
T2:finish
T3:work
T3:finish