Barieră
O altă primitivă de sincronizare folosită în calculul paralel este bariera. Ea are rolul de a se asigura că niciun thread nu poate trece mai departe de punctul în care este plasată decât atunci când toate thread-urile gestionate de barieră ajung în acel punct. Un exemplu de utilizare este atunci când împărțim un calcul pe mai multe thread-uri și vrem să nu mergem mai departe cu execuția programului decât în momentul în care fiecare thread și-a terminat propriile calcule.
În Pthreads, o barieră este reprezentată prin tipul pthread_barrier_t și inițializată prin următoarea funcție:
int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned count);
Primul parametru reprezintă o referință la barieră, al doilea parametru poate fi folosit pentru setarea unor atribute ale barierei (la fel ca la mutex), iar ultimul parametru denotă numărul de thread-uri care trebuie să ajungă la barieră pentru ca aceasta să se deblocheze. Acest lucru înseamnă că bariera are un contor intern care numără thread-urile care așteaptă deblocarea ei. Atunci când contorul ajunge la numărul setat la inițializarea barierei, thread-urile își pot continua execuția paralelă.
Pentru a dezaloca o barieră, se folosește următoarea funcție:
int pthread_barrier_destroy(pthread_barrier_t *barrier);
Ambele funcții returnează 0 dacă s-au executat cu succes sau un cod de eroare în caz contrar.
Pentru a face un thread să aștepte la o barieră (pentru a "pune o barieră" în cod), se folosește următoarea funcție:
int pthread_barrier_wait(pthread_barrier_t *barrier);
Funcția de mai sus va returna PTHREAD_BARRIER_SERIAL_THREAD pentru un singur thread arbitrar de la barieră și 0 pentru toate celelalte. Dacă funcția a avut vreo eroare, un cod eroare va fi returnat.
Fiecare thread care trebuie să aștepte la barieră va apela funcția de mai sus pe aceeași variabilă de tip pthread_barrier_t. Dacă numărul de thread-uri care apelează pthread_barrier_wait este mai mic decât parametrul cu care a fost inițializată bariera, aceasta nu se va debloca niciodată.
O reprezentare grafică a modului de funcționare a unei bariere se poate vedea în figura de mai jos, unde avem o barieră inițializată cu 2. Atunci când thread-ul T0 ajunge la barieră, acesta se blochează în așteptare, așa cum se observă în partea stângă a imaginii. La un moment dat de timp, T1 va ajunge și el la barieră, așa cum se vede în centrul imaginii. Abia în acel moment, cele două thread-uri își vor putea continua execuția individual, cum se observă în partea dreapta a imaginii.