Εργαστήριο 14. Συγχρονισμός Νημάτων (χρήση pthread_mutex_t, pthread_cond_t)

Σχετικά έγγραφα
Παράλληλη Επεξεργασία

Παράλληλη Επεξεργασία

Εθνικό Μετσόβιο Πολυτεχνείο Σχολή Ηλεκτρολόγων Μηχ. και Μηχανικών Υπολογιστών Εργαστήριο Υπολογιστικών Συστημάτων. Συγχρονισμός

Εθνικό Μετσόβιο Πολυτεχνείο Σχολή Ηλεκτρολόγων Μηχ. και Μηχανικών Υπολογιστών Εργαστήριο Υπολογιστικών Συστημάτων. Συγχρονισμός

Παρουσίαση 5 ης Άσκησης:

Παρουσίαση 5 ης Άσκησης:

Προγραμματισμός με Κοινόχρηστο Χώρο Διευθύνσεων 4

Συστήματα Παράλληλης και Κατανεμημένης Επεξεργασίας

Λειτουργικά Συστήματα

Λειτουργικά Συστήματα

Ελεγκτές/Παρακολουθητές (Monitors) Ταυτόχρονος Προγραμματισμός 1

ΛΕΙΤΟΥΡΓΙΚΑ ΣΥΣΤΗΜΑΤΑ. Διαδιεργασιακή Επικοινωνία Εργαστηριακές Ασκήσεις

Νήµατα. Πολύ σηµαντικό

Σηματοφόροι (Σηματοφορείς) Ταυτόχρονος Προγραμματισμός 1

ΕΘΝΙΚΟ ΚΑΙ ΚΑΠΟΔΙΣΤΡΙΑΚΟ ΠΑΝΕΠΙΣΤΗΜΙΟ ΑΘΗΝΩΝ ΤΜΗΜΑ ΠΛΗΡΟΦΟΡΙΚΗΣ & ΤΗΛΕΠΙΚΟΙΝΩΝΙΩΝ ΧΕΙΜΕΡΙΝΟ ΕΞΑΜΗΝΟ ΜΑΘΗΜΑ: ΛΕΙΤΟΥΡΓΙΚΑ ΣΥΣΤΗΜΑΤΑ

Σηματοφόροι (Σηματοφορείς) Ταυτόχρονος Προγραμματισμός 1

Συστήματα Παράλληλης και Κατανεμημένης Επεξεργασίας

Κατανεμημένα Συστήματα

Τμήμα Μηχανικών Πληροφορικής Τ.Ε. Σχολή Τεχνολογικών Εφαρμογών Ακαδημαϊκό έτος

Dr. Garmpis Aristogiannis - EPDO TEI Messolonghi

Εκφωνήσεις ασκήσεων εργαστηρίου 2 (pthreads)

ΣυγχρονισµόςσεΣυστήµατα ΠολλαπλώνΝηµάτων

Δομημένος Προγραμματισμός

Παράλληλη Επεξεργασία

HY-486 Αρχές Κατανεμημένου Υπολογισμού

Λειτουργικά Συστήματα. Ενότητα # 10: Προγραμματισμός UNIX Διδάσκων: Γεώργιος Ξυλωμένος Τμήμα: Πληροφορικής

Συγχρονισμός & σηματοφόροι. Προγραμματισμός II 1

Προγραμματισμός Ι. Δυναμική Διαχείριση Μνήμης. Δημήτρης Μιχαήλ. Ακ. Έτος Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

NIKOΛΑΟΣ ΝΤΙΡΛΗΣ 5ο ΦΡΟΝΤΙΣΤΗΡΙΟ ΑΙΘΟΥΣΑ Β4

Εισαγωγή εκτελέσιμου κώδικα σε διεργασίες

Προγραμματισμός Η/Υ 1 (Εργαστήριο)

- Το πρόγραµµα σας δίνει τα αναµενόµενα αποτελέσµατα.

HY-486 Αρχές Κατανεμημένου Υπολογισμού Εαρινό Εξάμηνο

#include <stdlib.h> Α. [-128,127] Β. [-127,128] Γ. [-128,128]

Νήµαταστην Java. Συγχρονισµός νηµάτων Επικοινωνία νηµάτων Εκτελέσιµα αντικείµενα Νήµατα δαίµονες Οµάδες νηµάτων. Κατανεµηµένα Συστήµατα 11-1

ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ Η/Υ Ακαδημαϊκό έτος ΤΕΤΡΑΔΙΟ ΕΡΓΑΣΤΗΡΙΟΥ #4

ΚΕΦΑΛΑΙΟ 9. Ταυτόχρονος προγραμματισμός και νήματα. 9.1 Εισαγωγή

Προγραμματισμός Ι (ΗΥ120)

ιαφάνειες παρουσίασης #5 (β)

Υλοποίηση Σηματοφόρων

ΛΕΙΤΟΥΡΓΙΚΑ ΣΥΣΤΗΜΑΤΑ. Διεργασίες και Νήματα Εργαστηριακές Ασκήσεις

Κεφάλαιο , 3.2: Συναρτήσεις II. (Διάλεξη 12)

Κεφάλαιο , 3.2: Συναρτήσεις II. ( ιάλεξη 12) ιδάσκων: ηµήτρης Ζεϊναλιπούρ

Θοδωρής Ανδρόνικος Τμήμα Πληροφορικής, Ιόνιο Πανεπιστήμιο

lab13grades Άσκηση 2 -Σωστά απελευθερώνετε ολόκληρη τη λίστα και την κεφαλή

Δομές Δεδομένων. Καθηγήτρια Μαρία Σατρατζέμη. Τμήμα Εφαρμοσμένης Πληροφορικής. Δομές Δεδομένων. Τμήμα Εφαρμοσμένης Πληροφορικής

Δομημένος Προγραμματισμός. Τμήμα Επιχειρηματικού Σχεδιασμού και Πληροφοριακών Συστημάτων

ΝΗΜΑΤΑ - ΕΡΓΑΣΤΗΡΙΟ 1 - ΣΗΜΕΙΩΣΕΙΣ

Δίκτυα Επικοινωνιών ΙΙ: Network Programming UDP Sockets, Signals

Προγραμματισμός συστημάτων UNIX/POSIX

Εργαστήριο ΔΙΕΡΓΑΣΙΕΣ - ΔΙΑΧΕΙΡΙΣΗ

Προγραμματισμός Ι. Είσοδος/Έξοδος. Δημήτρης Μιχαήλ. Ακ. Έτος Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

Εισαγωγή στον Προγραμματισμό

26/3/2012 Προγραμματισμός συστημάτων κοινόχρηστης μνήμης (Ι) Β. Δημακόπουλος

Δυναμική δέσμευση και αποδέσμευση μνήμης. Προγραμματισμός II 1

Μεθόδων Επίλυσης Προβλημάτων

ή α α POSIX Threads ( έ ος 1 ο )

Ελεγκτές/Παρακολουθητές (Monitors) Ταυτόχρονος Προγραμματισμός 1

Προγραμματισμός συστημάτων UNIX/POSIX. Σήματα (signals)

Λειτουργικά Συστήματα (ΗΥ222)

Θέτοντας και επιστρέφοντας την τιµή της προτεραιότητας διεργασίας

Λειτουργικά Συστήματα Η/Υ

Εργαστήριο Λειτουργικών Συστημάτων 8o εξάμηνο, Ροή Υ, ΗΜΜΥ

Λειτουργικά Συστήματα. Ενότητα # 2: Διεργασίες και Νήματα Διδάσκων: Γεώργιος Ξυλωμένος Τμήμα: Πληροφορικής

Δημιουργία & Τερματισμός Διεργασιών. Προγραμματισμός II 1

Λειτουργικά Συστήματα. Ενότητα # 2: Διεργασίες και Νήματα Διδάσκων: Γεώργιος Ξυλωμένος Τμήμα: Πληροφορικής

Αντικειμενοστρεφής Προγραμματισμός

2η Προγραµµατιστική Εργασία

Sheet2. Σωστή, και µπράβο που µεριµνήσατε για λίστες διαφορετικών µεγεθών.

Sheet2. - Άσκηση 1 οκ - Άσκηση 2 οκ. Σκέψου πώς θα µπορούσες να την

Λειτουργικά Συστήματα

Ανάπτυξη και Σχεδίαση Λογισμικού

ΕΡΓΑΣΤΗΡΙΟ 9: Συμβολοσειρές και Ορίσματα Γραμμής Εντολής

Ενδεικτική περιγραφή μαθήματος

Λειτουργικά Συστήματα 7ο εξάμηνο, Ακαδημαϊκή περίοδος

Εισαγωγή στον Προγραμματισμό

Μάθημα 3 ο ΔΙΕΡΓΑΣΙΕΣ (PROCESSES)

Διαδικασιακός Προγραμματισμός

ιεργασίες και νήµατα Προγραµµατισµός ΙΙΙ 1 lalis@inf.uth.gr

Προγραμματισμός ΙI (Θ)

Εικονική Μνήμη (Virtual Memory) Προγραμματισμός II 1

Επεξεργασία Αρχείων Κειµένου

Φροντιστήριο 4 Σκελετοί Λύσεων

ΠΑΝΕΠΙΣΤΗΜΙΟ ΘΕΣΣΑΛΙΑΣ ΣΧΟΛΗ ΘΕΤΙΚΩΝ ΕΠΙΣΤΗΜΩΝ ΤΜΗΜΑ ΠΛΗΡΟΦΟΡΙΚΗΣ

(Κεφάλαιο 2.7 και 12) Αρχεία στην C. (Διάλεξη 15)

Εργαστήριο 9: Αρχεία

Α' Εξάμηνο ΕΙΣΑΓΩΓΗ ΣΤΟ ΔΟΜΗΜΕΝΟ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟ

Δείκτες (Pointers) Ένας δείκτης είναι μια μεταβλητή με τιμή μια διεύθυνση μνήμης. 9.8

Ορισµός Νήµα (thread) είναι µια ακολουθιακή ροή ελέγχου (δηλ. κάτι που έχει αρχή, ακολουθία εντολών και τέλος) σ ένα

Κεφάλαιο 3. Διδακτικοί Στόχοι

Διάλεξη Εισαγωγή στη Java, Μέρος Γ

Αδιέξοδα (Deadlocks)

Βιβλιοθήκες Αφηρημένοι τύποι δεδομένων. Προγραμματισμός II 1

Προγραμματισμός Η/Υ (ΤΛ2007 )

Προγραμματισμός Ι. Δομές Δεδομένων. Δημήτρης Μιχαήλ. Ακ. Έτος Τμήμα Πληροφορικής και Τηλεματικής Χαροκόπειο Πανεπιστήμιο

Εικονική Μνήμη (Virtual Memory) Προγραμματισμός II 1

Εισαγωγή στον δομημένο προγραμματισμό

Προγραμματισμός Η/Υ 1 (Εργαστήριο)

Εργαστήριο 5. Εαρινό Εξάμηνο

FAIL PASS PASS οριακά

Transcript:

Εργαστήριο 14 Συγχρονισμός Νημάτων (χρήση pthread_mutex_t, pthread_cond_t) Να γράψετε πρόγραμμα που να δημιουργεί 1 νήμα Έτσι στο πρόγραμμα σας θα υπάρχουν 2 νήματα (το ένα νήμα είναι το αρχικό νήμα που εκτελεί τη main) Το ένα νήμα (αρχικό) θα παίζει το ρόλο του «παραγωγού» το οποίο θα δημιουργεί και θα βάζει 10 κόμβους (nodes) σε μια ουρά (υλοποιημένη σαν συνδεδεμένη λίστα) Στον κάθε κόμβο θα αποθηκεύεται ένας αριθμός από το 1 έως το 10 Το άλλο νήμα σαν «καταναλωτής» θα αφαιρεί τους κόμβους από την ουρά και θα εμφανίζει τα περιεχόμενά τους στην οθόνη Σημειώσεις: Κάθε κόμβος θα αποτελείται από μια δομή όπως φαίνεται πιο κάτω: struct node { int n_number; struct node *n_next Κάθε νήμα θα πρέπει να κλειδώνει με χρήση δυαδικών σηματοφόρων ή με άλλα λόγια μεταβλητών αμοιβαίου αποκλεισμού (mutual exclusion) τα κοινόχρηστα δεδομένα προτού αποκτήσει πρόσβαση πάνω τους Κάθε φορά που το αρχικό νήμα θα τοποθετεί στην ουρά ένα κόμβο, θα πρέπει να σηματοδοτεί μια συνθήκη (pthread_cond_t) στην οποία το άλλο νήμα περιμένει Περισσότερες πληροφορίες για τις μεταβλητές συνθήκης δείτε πιο κάτω Οι μεταβλητές συνθήκης (condition variables), παρέχουν ένα άλλο τρόπο συγχρονισμού νημάτων Σε αντίθεση με τις μεταβλητές αμοιβαίου αποκλεισμού, οι οποίες χρησιμοποιούνται για το συγχρονισμό των νημάτων ελέγχοντας την πρόσβαση στα προστατευμένα δεδομένα, οι μεταβλητές συνθήκης επιτρέπουν στα νήματα να συγχρονισθούν με βάση τη τιμή των δεδομένων αυτών Χωρίς την ύπαρξη των μεταβλητών συνθήκης, ο προγραμματιστής θα έπρεπε να ορίζει τα νήματα να ελέγχουν συνέχει τη τιμή μιας κοινής μεταβλητής για μια συγκεκριμένη τιμή ώστε να ελέγξουν αν έχει ικανοποιηθεί μια συγκεκριμένη συνθήκη Ο τρόπος αυτός είναι γνωστός ως απασχολημένη αναμονή (busy wait) και καταναλώνει πολύ περισσότερους πόρους συστήματος από ότι είναι αναγκαίο αφού όλα τα νήματα είναι απασχολημένα κάνοντας συνεχείς ελέγχους Οι μεταβλητές συνθήκης επιτρέπουν να επιτευχθεί ο σκοπός αυτός χωρίς τα νήματα να τίθενται σε συνεχή αναμονή Η μεταβλητή συνθήκης χρησιμοποιείται πάντα σε συνεργασία με μια μεταβλητή αμοιβαίου αποκλεισμού Για τη δημιουργία και την καταστροφή μιας μεταβλητής συνθήκης χρησιμοποιούνται οι παρακάτω συναρτήσεις: int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); int pthread_cond_destroy(pthread_cond_t *cond); Αφού μια μεταβλητή συνθήκης δηλωθεί ως pthread_cond_t, πρέπει να αρχικοποιηθεί με την pthread_cond_init πριν την χρήση της Μετά τη χρήση της μεταβλητής συνθήκης, οι πόροι που έχουν δεσμευθεί μπορούν να ελευθερωθούν με τη συνάρτηση pthread_cond_destroy Για την αναμονή μέχρι την ικανοποίηση μια συνθήκης και για τη σηματοδότησή της χρησιμοποιούνται οι πιο κάτω συναρτήσεις:

int pthread_cond_wait(pthread cond t *cond, pthread mutex t *mutex) int pthread_cond_signal(pthread cond t *cond) int pthread_cond_broadcast(pthread cond t *cond) Η συνάρτηση pthread_cond_wait θέτει το νήμα (έστω Α) που την καλεί σε αναμονή μέχρι τη σηματοδότησή της μεταβλητής συνθήκης cond Η συνάρτηση αυτή πρέπει να κληθεί αφού η μεταβλητή αμοιβαίου αποκλεισμού mutex έχει κλειδωθεί (pthread_mutex_lock) Όταν καλεστεί η συνάρτηση pthread_cond_wait, η μεταβλητή mutex ξεκλειδώνεται αυτόματα και το νήμα Α που την κάλεσε μπλοκάρει Έτσι μπορεί άλλο νήμα (έστω Β) να κλειδώσει τη mutex (pthread_mutex_lock) για να έχει πρόσβαση στον κοινό πόρο Όταν το νήμα Β ικανοποιήσει τη συνθήκη την οποία αναμένει το μπλοκαρισμένο νήμα Α τότε το νήμα Β πρέπει να καλέσει τη συνάρτηση pthread_cond_signal ή pthread_cond_broadcast Για να κληθεί οποιαδήποτε από τις 2 αυτές συναρτήσεις πρέπει η μεταβλητή mutex (στο νήμα Β) να είναι κλειδωμένη Η συνάρτηση pthread_cond_signal στέλνει ένα σήμα στο μπλοκαρισμένο νήμα Α για να μπορέσει να ξεμπλοκάρει Για να ξεμπλοκάρει το νήμα Α πρέπει πρώτα το νήμα Β μετά την κλήση της pthread_cond_signal να ξεκλειδώσει τη mutex (pthread_mutex_unlock) Όταν γίνει αυτό, τότε η mutex ξανακλειδώνεται αυτόματα στο νήμα Α και μπορεί να συνεχίσει για να έχει πρόσβαση στον κοινό πόρο Όταν τελειώσει το νήμα Α τότε πρέπει να ξεκλειδώσει τη μεταβλητή mutex (pthread_mutex_unlock) με τη σειρά του Η κλήση της συνάρτησης pthread_cond_signal ξεμπλοκάρει τουλάχιστον ένα από τα νήματα που είναι μπλοκαρισμένα στη μεταβλητή συνθήκης cond (αν υπάρχουν νήματα μπλοκαρισμένα στη μεταβλητή cond) Η κλήση της συνάρτησης pthread_cond_broadcast ξεμπλοκάρει όλα τα νήματα που είναι στη τρέχουσα φάση μπλοκαρισμένα στη μεταβλητή συνθήκης cond Δείτε παράδειγμα πιο κάτω #include <stdlibh> #include <stdioh> #include <stringh> #include <signalh> #include <pthreadh> /* compile with gcc mutexcondc -o mutexcond -Wall -lpthread */ pthread_cond_t cond; pthread_mutex_t mutex; void *thread(void *v) { printf("locking and waiting Type unlock to unlock me\n"); pthread_mutex_lock(&mutex); pthread_cond_wait(&cond, &mutex); printf("i've been unlocked\n"); pthread_mutex_unlock(&mutex); return NULL; int main() {

char cmd[10]; pthread_t t; pthread_mutex_init(&mutex, NULL); pthread_cond_init(&cond, NULL); cond printf("type lock to run a thread that locks and waits\n"); printf("type unlock to unlock the same thread\n"); printf("type quit to exit program\n"); while(fscanf(stdin, "%s", cmd)!= EOF) { if(strcmp(cmd, "lock") == 0) { // create thread pthread_create(&t, NULL, thread, NULL); else if(strcmp(cmd, "unlock") == 0) { // unblock one thread blocked on the condition variable pthread_cond_signal(&cond); else if(strcmp(cmd, "quit") == 0) { break; pthread_mutex_destroy(&mutex); pthread_cond_destroy(&cond); return 0;

Μέθοδοι διαχείρισης νημάτων σε πολυνηματικές εφαρμογές Στα πλαίσια του εργαστηρίου αυτού θα θεωρήσουμε ότι η πολυνηματική εφαρμογή που μας ενδιαφέρει είναι ένας εξυπηρετητής ιστού Ο εξυπηρετητής θα πρέπει να είναι σε θέση να εξυπηρετεί «ταυτόχρονα» πολλές αιτήσεις από πελάτες Δεν πρέπει, δηλαδή, να τελειώσει πρώτα με την εξυπηρέτηση μιας αίτησης και μετά να δέχεται νέες Για να το πετύχουμε αυτό, θα πρέπει να εκμεταλλευτούμε τη δυνατότητα ύπαρξης πολλών νημάτων μέσα στη διεργασία του εξυπηρετητή Στο κείμενο που ακολουθεί περιγράφονται δύο διαφορετικοί τρόποι με τους οποίους μπορούμε να προσεγγίσουμε τη διαχείριση των νημάτων: 1 Τεχνική Α: Δημιουργία ενός καινούργιου νήματος για την επεξεργασία μιας αίτησης, ενώ το αρχικό νήμα περιμένει νέες αιτήσεις 2 Τεχνική B: Δημιουργία ενός thread-pool για τη διαχείριση των νέων αιτήσεων Για την περίπτωση αυτή θα δούμε δυο διαφορετικές ιδέες υλοποίησης Στα πλαίσια της εργαστηριακής άσκησης 4 για τη δημιουργία ενός εξυπηρετητή ιστού όλες οι προσεγγίσεις είναι αποδεκτές (δεν είναι δηλαδή απαραίτητο να χρησιμοποιηθεί thread-pool) όσον αφορά τη λειτουργία του προγράμματος Όμως η μη χρησιμοποίηση thread-pool ενδέχεται να σας στοιχίσει βαθμούς στο κομμάτι της επίδοσης και της αρχιτεκτονικής σχεδίασης του συστήματος Τεχνική Α: Δημιουργία ενός καινούργιου νήματος για την επεξεργασία μιας αίτησης, ενώ το αρχικό νήμα περιμένει νέες αιτήσεις Η προσέγγιση αυτή αποτελεί τον απλούστερο τρόπο πολυνηματικής εφαρμογής Υπάρχει το αρχικό νήμα το οποίο περιμένει αιτήσεις από πελάτες (εκτός από το νήμα αυτό μπορεί να υπάρχουν και άλλα νήματα τα οποία διαχειρίζονται άλλου είδους εργασίες πέραν της εξυπηρέτησης πελατών, όπως πχ, της διαχείρισης γραμμής εντολών) Για κάθε εισερχόμενη αίτηση το αρχικό νήμα δημιουργεί ένα καινούργιο νήμα το οποίο εξυπηρετεί τον αιτητή, και με το πέρας της αποστολής του το νήμα αυτό τερματίζει τη λειτουργία του Η προσέγγιση αυτή δεν είναι πολύ καλή για τους ακόλουθους λόγους: 1 Η δημιουργία και καταστροφή νημάτων δεν είναι ιδιαίτερα ελεγχόμενη, γεγονός που μπορεί να αποβεί εξαιρετικά προβληματικό σε κάποιες περιπτώσεις 2 Η δημιουργία ενός καινούργιου νήματος είναι ακριβή (παρόλο που είναι πιο φθηνή από τη δημιουργία μιας νέας διεργασίας) Στην συνεχεία πρέπει να αποδεσμεύσουμε τα δεδομένα που σχετίζονται με τον νήμα και τέλος να το απελευθερώσουμε, γεγονός που επιφέρει μεγαλύτερο κόστος 3 Ανά πάσα στιγμή δεν μπορούμε να ξέρουμε τον μέγιστο αριθμό νημάτων, επομένως μπορεί η διεργασία του εξυπηρετητή να ξεπεράσει το επιτρεπτό όριο μνήμης με αποτέλεσμα να χάσουμε την υπηρεσία Τεχνική Β: Δημιουργία ενός thread-pool για τη διαχείριση των νέων αιτήσεων Η δεύτερη προσέγγιση αφορά τη δημιουργία ενός thread-pool από το αρχικό νήμα Με τον όρο thread-pool, αναφερόμαστε στην δημιουργία ενός σταθερού αριθμού νημάτων-εργατών (ο αριθμός επιλέγεται ανάλογα με την εφαρμογή) Τα νήματα που βρίσκονται στο thread-pool συναγωνίζονται για την εξυπηρέτηση κάθε καινούργιας αίτησης Δηλαδή κάθε καινούργια αίτηση ανατίθεται σε κάποιο από τα νήματα που δεν έχει δουλειά Τα νήματα του thread-pool, αφού εξυπηρετήσουν ένα πελάτη, δεν τερματίζουν, αλλά μεταβαίνουν σε κατάσταση αναμονής

για να εξυπηρετήσουν άλλο πελάτη Αν δεν υπάρχει διαθέσιμο νήμα, το αρχικό θα πρέπει να περιμένει μέχρι να υπάρξει, χωρίς να δέχεται νέες αιτήσεις Πιο συγκεκριμένα εάν υπάρξουν περισσότερες αιτήσεις από το μέγιστο αριθμό νημάτων στο pool τότε το σύστημα απορρίπτει την αίτηση κλείνοντας το socket (χωρίς να επιστρέφει οποιανδήποτε απάντηση στον πελάτη) Για τη διαχείριση των νημάτων του pool και την εξυπηρέτηση των αιτήσεων μπορούμε να χρησιμοποιήσουμε μεταξύ άλλων τις πιο κάτω μεθόδους: Τεχνική Β1: Μοντέλο παραγωγού-καταναλωτή: 1 Τα νήματα εργάτες δημιουργούνται από την αρχή στον εξυπηρετητή και τα thread IDs τους αποθηκεύονται σε ένα πίνακα 2 Ο έλεγχος ταυτοχρονίας υπακούει στο μοντέλο παραγωγού καταναλωτή (producer - consumer) χρησιμοποιώντας ένα αριθμό δυαδικών σηματοφόρων (τα οποία προσφέρονται από την βιβλιοθήκη νημάτων) Τα νήματα τα οποία δεν εργάζονται σε κάποια δεδομένη στιγμή είναι μπλοκαρισμένα πάνω σε ένα από αυτούς τους σηματοφόρους Ο σηματοφόρος που είναι υπεύθυνος για αυτή τη δουλειά κλειδώνεται πριν τη δημιουργία των νημάτων εργατών, με αποτέλεσμα τα νήματα να είναι μπλοκαρισμένα πριν την αποδοχή μιας αίτησης Μόλις έρθει μια νέα σύνδεση, το κυρίως νήμα ξεκλειδώνει αυτό το σηματοφόρο και το νήμα εργάτης που αναλαμβάνει την αίτηση τον ξανακλειδώνει 3 Οι υπόλοιποι σηματοφόροι χρησιμοποιούνται για έλεγχο των κρίσιμων σημείων στο κώδικα 4 Για να μπορέσουμε να περάσουμε το new_socket που δημιουργείται από την κλήση συστήματος accept(), στο κυρίως νήμα, σε ένα από τα νήματα εργάτες ορίζουμε το new_socket ως καθολική μεταβλητή Τεχνική Β2: Χρήση Ουράς από Εργασίες: Στη μέθοδο αυτή, το thread-pool υλοποιείται ως μια ουρά από εργασίες που εκκρεμούν Είναι μια δομή στην οποία πέραν από τον πίνακα με τα νήματα εργάτες και τους σηματοφόρους ελέγχου έχουμε και μια ουρά εργασιών Κάθε εργασία αναπαριστάται από μια δομή δεδομένων (struct) η οποία περιέχει μεταξύ άλλων τη συνάρτηση την οποία θα καλεστεί από το νήμα εργάτη, τις παραμέτρους της συνάρτησης αυτής και δείκτη στην επόμενη εργασία Η εργασία μπορεί να οριστεί όπως πιο κάτω:

struct tp_task { void* (*task_func)(void*);/* Η συνάρτηση που είναι υπεύθυνη για την εργασία αυτή */ void* task_arg; /* Η παράμετροι για την εργασία αυτή */ struct tp_task* next; /* Η επόμενη εργασία */ ; Η δομή του thread-pool μπορεί να οριστεί όπως πιο κάτω: struct threadpool { size_t num_threads; // Ο αριθμός των νημάτων size_t max_queue_size; // Το μέγιστο μέγεθος της ουράς size_t cur_queue_size; // Το μέγεθος της ουράς struct threadpool_work* queue_head; // Η επόμενη εργασία που θα εκτελεστεί struct threadpool_work* queue_tail; // Η πιο νέα εργασία στην ουρά pthread_t* threads; // Ο πίνακας με τα νήματα pthread_mutex_t queue_lock; // Σηματοφόροι ; Στη μέθοδο αυτή, όταν η ουρά δεν έχει εργασίες για να τύχουν επεξεργασίας, τα νήματα εργάτες είναι αδρανή Με την είσοδο μιας εργασίας στην ουρά ο σηματοφόρος ξεκλειδώνει και ένα νήμα εργάτης αναλαμβάνει την εξυπηρέτηση του Για να πετύχουμε τα πιο πάνω χρειαζόμαστε μια συνάρτηση η οποία θα είναι υπεύθυνη για την εισαγωγή διεργασιών και ξεκλειδώματος των σηματοφόρων Επίσης χρειαζόμαστε μια άλλη συνάρτηση η οποία θα τρέχει τον κώδικα των νημάτων