Σύγχρονη επικοινωνία με ανταλλαγή μηνυμάτων (CSP message passing model) Ταυτόχρονος Προγραμματισμός 1 lalis@inf.uth.gr
Σύγχρονα κανάλια επικοινωνίας Μέρος του Communicating Sequential Processes (CSP) που πρότεινε ο Hoare ως φορμαλισμό για την περιγραφή ταυτόχρονων διεργασιών/συστημάτων To CSP είναι ένα θεωρητικό εργαλείο Που επηρέασε τον σχεδιασμό πολλών πραγματικών συστημάτων και γλωσσών προγραμματισμού Occam, Ada, Plan9/Inferno/Limbo, Go Σημείωση: η σύνταξη που θα χρησιμοποιήσουμε είναι απλοποιημένη και ενδεικτική -- δεν αντιστοιχεί στην «επίσημη» σύνταξη/σημασιολογία του CSP Ταυτόχρονος Προγραμματισμός 2 lalis@inf.uth.gr
Μοντέλο channel P1 P2 Αλληλεπίδραση μέσω καναλιών επικοινωνίας Κάθε κανάλι είναι σημείο-προς-σημείο: συνδέει δύο νήματα/διεργασίες αξιόπιστο: δεν χάνονται μηνύματα μιας κατεύθυνσης (half duplex) Τα κανάλια μεταφέρουν μηνύματα που έχουν συγκεκριμένο τύπο (εσωτερική δομή) Το σύνολο των συνεργαζόμενων νημάτων/διεργασιών και καναλιών θεωρείται γνωστό εκ των προτέρων Ταυτόχρονος Προγραμματισμός 3 lalis@inf.uth.gr
Βασικές λειτουργίες chan!msg Αποστολή μηνύματος msg στο κανάλι chan Ο αποστολέας μπλοκάρεται μέχρι το μήνυμα να παραληφθεί από την διεργασία που βρίσκεται στην άλλη άκρη του καναλιού chan?msg Παραλαβή μηνύματος msg από το κανάλι chan Ο παραλήπτης μπλοκάρεται μέχρι η διεργασία που βρίσκεται στην άλλη άκρη του καναλιού να στείλει ένα μήνυμα συμβατού τύπου Το msg μπορεί είναι μια τιμή ή μια μεταβλητή στην οποία αποθηκεύεται το περιεχόμενο του μηνύματος Ταυτόχρονος Προγραμματισμός 4 lalis@inf.uth.gr
Συγχρονισμός αποστολής/παραλαβής Η αποστολή και παραλαβή ενός μηνύματος μπορεί να θεωρηθεί ως ένα συμμετρικό ραντεβού Η διεργασία που επιχειρεί πρώτη την επικοινωνία θα περιμένει μέχρι η άλλη διεργασία θελήσει να πραγματοποιήσει την «αντίστροφη» επικοινωνία Κάθε κανάλι έχει το πολύ ένα μήνυμα που βρίσκεται καθοδόν, από τον αποστολέα προς τον παραλήπτη Δεν χρειάζονται ενδιάμεσες αποθήκες Μπορεί όμως να υπάρξουν αδιέξοδα! Ταυτόχρονος Προγραμματισμός 5 lalis@inf.uth.gr
Ραντεβού: όποια διεργασία «φτάσει» πρώτη στο «σημείο συνάντησης» περιμένει την άλλη P2 exec path P1 exec path chan!msg chan?msg «σημείο» συνάντησης Ταυτόχρονος Προγραμματισμός 6 lalis@inf.uth.gr
int a; a = 5; P1 P2 int b; chan!a <int,5> chan?b (b is 5) Ταυτόχρονος Προγραμματισμός 7 lalis@inf.uth.gr
int a; a = 5; P1 P2 int b; chan?b chan!a <int,5> (b is 5) Ταυτόχρονος Προγραμματισμός 8 lalis@inf.uth.gr
Παραγωγός-καταναλωτής P Data C Ταυτόχρονος Προγραμματισμός 9 lalis@inf.uth.gr
P: Data d; /* produce d */ chan!d; C: Data d; chan?d; /* consume d */ Ταυτόχρονος Προγραμματισμός 10 lalis@inf.uth.gr
Αποφυγή μπλοκαρίσματος Αν ο παραγωγός επιχειρήσει να στείλει δεδομένα ενώ δεν περιμένει ο καταναλωτής, θα μπλοκάρει Η σύγχρονη επικοινωνία «δένει» στενά τα εμπλεκόμενα μέρη και εμποδίζει την υλοποίηση πιο ευέλικτων σχημάτων αλληλεπίδρασης Eιδική δομή ταυτόχρονης αναμονής για την παραλαβή μηνύματος από διαφορετικές διεργασίες Ανάλογα με τον αποστολέα, μπορεί να γίνεται αναμονή για μήνυμα διαφορετικού τύπου Ταυτόχρονος Προγραμματισμός 11 lalis@inf.uth.gr
Ταυτόχρονη αναμονή για παραλαβή μηνύματος από διαφορετικά κανάλια select { chan 1?msg 1 : S 1 chan 2?msg 2 : S 2 chan N?msg N : S N Η σειρά ελέγχου των περιπτώσεων στην δομή αναμονής είναι μη ντετερμινιστική αλλά δίκαιη Η select τερματίζει όταν παραληφθεί ένα από τα μηνύματα μέσω του προδιαγεγραμμένου καναλιού, και εκτελεστεί η αντίστοιχη ομάδα εντολών Ταυτόχρονος Προγραμματισμός 12 lalis@inf.uth.gr
Αναμονή πάνω σε σύνολο καναλιών chan[]?msg,idx Επιστρέφει το μήνυμα msg και την θέση idx (στον πίνακα) του καναλιού από το οποίο έφτασε το μήνυμα αν δεν ενδιαφέρει η θέση idx τότε βάζουμε void Ισοδύναμο με: select { chan[0]?msg: idx=0; chan[1]?msg: idx=1; chan[n-1]?msg: idx=n-1; Ταυτόχρονος Προγραμματισμός 13 lalis@inf.uth.gr
Ταυτόχρονη αναμονή για παραλαβή μηνύματος υπό συνθήκη select { cond 1 && chan1?msg1: S 1 cond 2 && chan2?msg2: S 2 cond N && chann?msgn: S N Για να παραληφθεί ένα μήνυμα (αν είναι διαθέσιμο) πρέπει η αντίστοιχη συνθήκη να είναι αληθής Η σειρά ελέγχου των διαφόρων περιπτώσεων στην select είναι μη ντετερμινιστική αλλά δίκαιη Κάποιες περιπτώσεις παραλαβής μπορεί να μην έχουν συνθήκη, οπότε είναι διαρκώς «ενεργοποιημένες» Ταυτόχρονος Προγραμματισμός 14 lalis@inf.uth.gr
Παραγωγός-καταναλωτής με αποθήκη P Data Buf "GET" C Data Ταυτόχρονος Προγραμματισμός 15 lalis@inf.uth.gr
Buf: Data buf[n]; int in = 0, out = 0, n = 0; select { (n<n) && chanp?buf[in]: { in = (in+1)%n; n++; (n>0) && chanc?"get": { chanc!buf[out]; out = (out+1)%n; n--; P: /* produce d */ chanp!d; C: chanc!"get"; chanc?d; /* consume d */ Ταυτόχρονος Προγραμματισμός 16 lalis@inf.uth.gr
Πολλοί παραγωγοί-καταναλωτές P1 C1 P2 Data Buf "GET" C2 Data...... PN CM Ταυτόχρονος Προγραμματισμός 17 lalis@inf.uth.gr
Buf: Data buf[n]; int in = 0, out = 0, n = 0; int pid; select { (n<n) && chanp[]?buf[in],void: { in = (in+1)%n; n++; (n>0) && chanc[]?"get",pid: { chanc[pid]!buf[out]; out = (out+1)%n; n--; Pi: /* produce d */ chanp[i]!d; Ci: chanc[i]!"get"; chanc[i]?d; /* consume d */ Ταυτόχρονος Προγραμματισμός 18 lalis@inf.uth.gr
Αμοιβαίος αποκλεισμός Lock "LOCK" "UNLOCK" P1 P2 PN... Ταυτόχρονος Προγραμματισμός 19 lalis@inf.uth.gr
Lock: int pid=-1; select { (pid == -1) && chan[]?"lock",pid: { (pid!= -1) && chan[pid]?"unlock",void { pid = -1; Pi: chan[i]!"lock"; /* CS */ chan[i]!"unlock"; Ταυτόχρονος Προγραμματισμός 20 lalis@inf.uth.gr
Lock: int pid=-1; Pi: chan[i]!"lock"; /* CS */ χρειάζονται αυτές οι συνθήκες αναμονής; select { (pid == -1) && chan[]?"lock",pid: { (pid!= -1) && chan[pid]?"unlock": { pid = -1; chan[i]!"unlock"; Ταυτόχρονος Προγραμματισμός 21 lalis@inf.uth.gr
Lock: chan[]?"lock",void; chan[]?"unlock",void; Pi: chan[i]!"lock"; /* CS */ chan[i]!"unlock"; Ταυτόχρονος Προγραμματισμός 22 lalis@inf.uth.gr
Γενικός σηματοφόρος Sem "DOWN" "UP" P1 P2 PN... Ταυτόχρονος Προγραμματισμός 23 lalis@inf.uth.gr
Sem: int val = ; select { (val>0) && chan[]?"down",void: { val--; chan[]?"up",void: { val++; Pi: chan[i]!"down"; Pj: chan[j]!"up"; Ταυτόχρονος Προγραμματισμός 24 lalis@inf.uth.gr
Συνδαιτυμόνες φιλόσοφοι P0 P1 PN-1... "GET" "PUT" Fork0 Fork1 Fork2 ForkN-1... Ταυτόχρονος Προγραμματισμός 25 lalis@inf.uth.gr
Fork(i): 0<= i < N select { chan[(i*2-1)%n]?"get": chan[(i*2-1)%n]?"put"; chan[i*2]?"get": chan[i*2]?"put"; Pi : 0<= i < N chan[i*2]!"get"; chan[i*2+1]!"get"; /* eat */ chan[i*2]!"put"; chan[i*2+1]!"put"; Ταυτόχρονος Προγραμματισμός 26 lalis@inf.uth.gr
Κόσκινο του Ερατοσθένη filter multiples of 1st prime filter multiples of 2nd prime filter multiples of 3rd prime filter multiples of 4th prime sink Sieve1 Sieve2 Sieve3 Sieve4 Sieve5 2 3 5 7 (11) 2 2 3 3 4 5 5 6 7 7 8 9 10 11 12 13 11 13 Ταυτόχρονος Προγραμματισμός 27 lalis@inf.uth.gr
Sieve0: seed Sieve1: filter 2 Sieve2: filter 3 Sieve3: filter 5 Sieve4: filter 7 Sieve5: sink Ταυτόχρονος Προγραμματισμός 28 lalis@inf.uth.gr
Sieve(0): (seed) for (n=2; n<=100; n++) { chan[0]!n; Sieve(5): (sink) int n; chan[4]?n; print(n); Sieve(i): 1<= i <=4 int p, mp, n; chan[i-1]?p; // prime print(p); mp = p*p; // start filter chan[i-1]?n; while (n > mp) { mp = mp+p; // advance filter if (n < mp) { chan[i]!n; // passed filter Ταυτόχρονος Προγραμματισμός 29 lalis@inf.uth.gr
Πολλαπλασιασμός πινάκων 1 2 3 4 5 6 7 8 9 1 0 2 0 1 2 1 0 0 = 4 2 6 10 5 18 16 8 30 2 0 1 2 1 0 0 0 1 Zero 0,0,0 MulAdd 8,0,4 MulAdd 18,5,4 MulAdd 18,5,10 Result 4 5 6 Ταυτόχρονος Προγραμματισμός 30 lalis@inf.uth.gr
Source 102 2 0 1 Source 012 2 1 0 Source 100 0 0 1 0,0,0 Zero MulAdd 2,0,1 MulAdd 6,2,1 MulAdd 6,2,4 Result 1 2 3 2 2 0 0 Vi,j 1 0 1 0 1 0,0,0 Zero MulAdd 8,0,4 MulAdd 18,5,4 MulAdd 18,5,10 Result 4 Hi,j 5 Hi,j+1 6 2 2 0 0 Vi+1,j 1 0 1 0 1 0,0,0 Zero MulAdd 14,0,7 MulAdd 30,8,7 MulAdd 30,8,16 Result 7 8 9 2 2 0 0 1 0 1 0 1 Sink Sink Sink Ταυτόχρονος Προγραμματισμός 31 lalis@inf.uth.gr
Source(i): 0<= i <N for (k=0; k<n-1; k++ ) { chanv[0][i]!b[i][k]; Sink(i): 0<= i <N for (k=0; k<n-1; k++ ) { chanv[n][i]?dmy; Zero(i): 0<= i <N for (k=0; k<n-1; k++ ) { chanh[i][0]!0; Result(i): 0<= i <N MulAdd(i,j): 0<= i,j <N int s,b; for (k=0; k<n-1; k++) { chanv[i][j]?b; // B[i][k] chanh[i][j]?s; // sum s = s + A[i][j]*b; chanv[i+1][j]!b; chanh[i][j+1]!s; for (k=0; k<n-1; k++ ) { chanh[i][n]?r[i][k]; Ταυτόχρονος Προγραμματισμός 32 lalis@inf.uth.gr
Υλοποίηση CSP Τοπικά: Κανάλι επικοινωνίας είναι μια ουρά μηνυμάτων Οι λειτουργίες!/? «αντιστοιχούν» στην τοποθέτηση και απομάκρυνση μηνυμάτων στην / από την ουρά Η select μπορεί να υλοποιηθεί ελέγχοντας τον τύπο ή/και το περιεχόμενο των μηνυμάτων Κατανεμημένα: Επικοινωνία πάνω από δίκτυο Ουρά σε εξυπηρετητή ή παραλήπτη Όταν ο παραλήπτης απομακρύνει ένα μήνυμα, ειδοποιείται ο αποστολέας για να ξεμπλοκάρει Ταυτόχρονος Προγραμματισμός 33 lalis@inf.uth.gr
P1 P2 CSP API Msg Queue Ταυτόχρονος Προγραμματισμός 34 lalis@inf.uth.gr
P1 P2 CSP API CSP API Server Msg Queue Client Client Ταυτόχρονος Προγραμματισμός 35 lalis@inf.uth.gr
P1 P2 CSP API CSP API Msg Queue Ταυτόχρονος Προγραμματισμός 36 lalis@inf.uth.gr
Κανάλια με πολυπλεξία P1 channel P3 P2 P4 Το ίδιο κανάλι μπορεί να χρησιμοποιηθεί από πολλές διεργασίες για την αποστολή/παραλαβή μηνυμάτων Η αποστολή/παραλαβή παραμένει σύγχρονη Κάθε μήνυμα μπορεί να παραληφθεί μόνο μια φορά Ταυτόχρονος Προγραμματισμός 37 lalis@inf.uth.gr
Παραγωγός-καταναλωτής με αποθήκη Buf: Data buf[n]; int in = 0, out = 0, n = 0; select { (n<n) && chan?("putdata",buf[in]): { in = (in+1)%n; n++; (n>0) && chan?"getdata": { chan!("data",buf[out]); out = (out+1)%n; n--; P: /* produce d */ chan!("putdata",d); C: chan!"getdata"; chan?("data",d); /* consume d */ Ταυτόχρονος Προγραμματισμός 38 lalis@inf.uth.gr
Αμοιβαίος αποκλεισμός Lock: chan?"lock"; chan?"unlock"; Pi: chan!"lock"; /* CS */ Lock: chan!"lock"; chan?"unlock"; Pi: chan?"lock"; /* CS */ chan!"unlock"; chan!"unlock"; Ταυτόχρονος Προγραμματισμός 39 lalis@inf.uth.gr
Φιλόσοφοι Pi : Fork(i): chan[i]!"get": chan[i]?"put"; chan[i]?"get"; chan[(i+1)%n]?"get"; /* eat */ chan[i]!"put"; chan[(i+1)%n]!"put"; Ταυτόχρονος Προγραμματισμός 40 lalis@inf.uth.gr
Ραντεβού στην Ada Ταυτόχρονος Προγραμματισμός 41 lalis@inf.uth.gr
Κλήση διαδικασίας με ραντεβού Συνδυασμός σχήματος συγχρονισμού του CSP για επικοινωνία μέσω κλήσεων διαδικασίας Μέσω της εντολής accept, γίνεται ρητή αποδοχή μιας κλήσης του API, με μπλοκάρισμα μέχρι ένα άλλο νήμα να πραγματοποιήσει μια τέτοια κλήση Αντίθετα, αν ένα νήμα καλέσει μια διαδικασία για την οποία δεν έχει γίνει ακόμα accept, τότε μπλοκάρει μέχρι η κλήση να γίνει αποδεκτή και να εκτελεστεί Ταυτόχρονος Προγραμματισμός 42 lalis@inf.uth.gr
η P1 περιμένει την P2 η P2 περιμένει την P1 P1 P2 P1 P2 f() accept f() accept f() f() επεξεργασία return επεξεργασία return Ταυτόχρονος Προγραμματισμός 43 lalis@inf.uth.gr
Συντακτικά στοιχεία Συνάρτηση διεπαφής f(in type in1 par in1,, type inn par inn, out type out1 par out1,, type outn par outn ); Κλήση f(in 1,,in N,&out 1,,&out N ) Αναμονή για κλήση και εκτέλεση accept f(in 1,,in N,&out 1,,&out N ) { /* υλοποίηση συνάρτησης */ Ταυτόχρονος Προγραμματισμός 44 lalis@inf.uth.gr
Αμοιβαίος αποκλεισμός lock: export down(),up(); accept down() { accept up() { Pi: lock.down(); /* κρίσιμο τμήμα */ lock.up(); Ταυτόχρονος Προγραμματισμός 45 lalis@inf.uth.gr
Παραγωγός καταναλωτής buf: export put(in Data d), get(out Data d); Data buf[n]; int in=0,out=0,n=0; accept put(in Data d) { buf[in] = d; in = (in+1)%n; accept get(out Data d) { d = buf[out]; out = (out+1)%n; producer: /* produce d */ buf.put(d); consumer: buf.get(&d); /* consume d */ Ταυτόχρονος Προγραμματισμός 46 lalis@inf.uth.gr
Σχόλιο για την προηγούμενη λύση Μέσω των διαδικασιών put και get γίνεται η μεταφορά των δεδομένων προς / από την αποθήκη Η αλλαγή των δεικτών in / out γίνεται έξω από τον κώδικα των διαδικασιών put και get δεν υπάρχει λόγος να παραμείνει μπλοκαρισμένο περισσότερο το νήμα που καλεί αυτές τις διαδικασίες Λόγω της σχετικής σειράς των εντολών accept, η μόνη εφικτή σειρά εκτέλεσης των πράξεων είναι put, get, put, get, put, get κτλ χρησιμοποιείται μόνο μια θέση της αποθήκης Ταυτόχρονος Προγραμματισμός 47 lalis@inf.uth.gr
Αναμονή με επιλογή σε πολλές κλήσεις Στο πνεύμα του CSP select { when C 1 accept foo 1 () Body 1 S 1 when C 2 accept foo 2 () Body 2 S 2 else S N ; Μπλοκάρει μέχρι να κληθεί μια διαδικασία της οποίας η συνθήκη επιλογής είναι αληθής ή να υπάρχει else Το νήμα που καλεί μια διαδικασία μπλοκάρεται μόνο κατά την εκτέλεση του σώματος Body i της, και όχι κατά την εκτέλεση των υπόλοιπων εντολών Si Ταυτόχρονος Προγραμματισμός 48 lalis@inf.uth.gr
Γενικός σηματοφόρος sem: export down(), up(); int val=0; select { when (val>0) accept down() { val--; when (true) accept up() { val++; Ταυτόχρονος Προγραμματισμός 49 lalis@inf.uth.gr
Παραγωγός καταναλωτής buf: export put(in Data d), get(out Data d); data buf[n]; int in=0,out=0,n=0; select { when (n<n) accept put(in Data d) { buf[in] = d; n++; in = (in+1)%n; when (n>0) accept get(out Data d) { d = buf[out]; n--; out = (out+1)%n; producer: /* produce d */ buf.put(d); consumer: buf.get(&d); /* consume d */ Ταυτόχρονος Προγραμματισμός 50 lalis@inf.uth.gr