POSIX Threads/Synchronizacja między wątkami/Bariery

Wstęp edytuj

Bariera jest mechanizmem synchronizacji grupy wątków. Wątki po dojściu bo bariery są wstrzymywane do czasu, aż ostatni jej nie osiągnie - wówczas wszystkie są kontynuowane. Z barierą związana jest liczba większa od zera określająca ile wątków wchodzi w skład grupy.

Gdy bariera jest osiągana przez wszystkie wątki, jej stan (licznik) jest automatycznie inicjowany, na taką wartość, jaką ustawiło ostatnie wywołanie pthread_barrier_init.

 

Inicjalizacja bariery edytuj

Inicjalizację bariery wykonuje funkcja pthread_barrier_init, usuwa zaś funkcja pthread_barrier_destroy. Bariera może mieć dodatkowe atrybut.

Typy edytuj

  • pthread_barrier_t
bariera
  • pthread_barrierattr_t
atrybuty bariery

Funkcje edytuj

  • int pthread_barrier_init(pthread_barrier_t *barrier, pthread_barrierattr_t* attr, unsigned int count)  (doc)
inicjalizacja bariery, count jest liczbą wątków w grupie, attr opcjonalne atrybuty
  • int pthread_barrier_destroy(pthread_barrier_t *barrier)  (doc)
usunięcie bariery

Atrybuty bariery edytuj

Gdy biblioteka implementuje opcję TSH, wówczas można ustalić, czy bariery mogą być współdzielone między procesami - domyślnie nie.

Funkcje edytuj

  • int pthread_barrierattr_init(pthread_barrierattr_t *attr)  (doc)
inicjacja atrybutów bariery
  • int pthread_barrierattr_destroy(pthread_barrierattr_t *attr)  (doc)
usunięcie atrybutów
  • int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared)  (doc)
ustawienie flagi współdzielenia z innymi procesami; pshared ma wartość PTHREAD_PROCESS_SHARED lub PTHREAD_PROCESS_PRIVATE (domyślnie)
  • int pthread_barrierattr_getpshared(pthread_barrierattr_t *attr, int *pshared)  (doc)
odczytanie flagi

Bariera edytuj

Wywołanie funkcji pthread_barrier_wait jest traktowane jako dojście do bariery - powoduje zwiększenie licznika związanego z barierą.

Gdy bariera zostanie osiągnięta przez wszystkie wątki, funkcja w jednym wątku (standard nie określa w którym) zwraca specjalną wartość PTHREAD_BARRIER_SERIAL_THREAD, pozostałe wartość 0.

Funkcje edytuj

  • int pthread_barrier_wait(pthread_barrier_t *barrier)  (doc)

Przykład edytuj

W programie bariera służy do wstrzymania programu, do czasu aż wszystkie utworzone wątki skończą działanie.

#define _POSIX_C_SOURCE 200809L

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#define test_errno(msg) do{if (errno) {perror(msg); exit(EXIT_FAILURE);}} while(0)

pthread_barrier_t	bariera;

void* watek(void* numer) {
	int s, status;

	s = rand() % 4 + 1; // oczekiwanie 1-4 s
	printf("\twątek #%d rozpoczęty, zostanie wstrzymany na %d sekund\n", (int)numer, s);

	sleep(s);

	printf("\twątek #%d osiągnął barierę\n", (int)numer);
	status = pthread_barrier_wait(&bariera);
	switch (status) {
		case 0: // ok
			break;

		case PTHREAD_BARRIER_SERIAL_THREAD:
			printf(
				"\twszystkie wątki osiągnęły barierę "
				"(PTHREAD_BARRIER_SERIAL_THREAD w wąteku #%d)\n",
				(int)numer
			);
			break;

		default:
			fprintf(stderr, "pthread_barrier_wait: %s\n", strerror(status));
			break;
	}
	return NULL;
}
//------------------------------------------------------------------------

#define N 10	/* liczba wątków */

int main() {
	int i;
	pthread_t	id[N];

	srand(time(NULL));

	printf("zostanie uruchomionych %d wątków\n", N);

	/* inicjalizacja bariery - N wątków */
	errno = pthread_barrier_init(&bariera, NULL, N);
	test_errno("pthread_barrier_init");

	/* utworzenie N wątków */
	for (i=0; i < N; i++) {
		errno = pthread_create(&id[i], NULL, watek, (void*)i);
		test_errno("pthread_create");
	}

	/* oczekiwaie na dojście do bariery wszystkich wątków */
	for (i=0; i < N; i++) {
		errno = pthread_join(id[i], NULL);
		test_errno("pthread_join");
	}

	/* zwolnienie bariery */
	errno = pthread_barrier_destroy(&bariera);
	test_errno("pthread_barrier_destroy");

	return EXIT_SUCCESS;
}
//------------------------------------------------------------------------

Przykładowe wyjście:

zostanie uruchomionych 10 wątków
	wątek #0 rozpoczęty, zostanie wstrzymany na 0 sekund
	wątek #0 osiągnął barierę
	wątek #1 rozpoczęty, zostanie wstrzymany na 0 sekund
	wątek #1 osiągnął barierę
	wątek #2 rozpoczęty, zostanie wstrzymany na 0 sekund
	wątek #2 osiągnął barierę
	wątek #3 rozpoczęty, zostanie wstrzymany na 0 sekund
	wątek #3 osiągnął barierę
	wątek #4 rozpoczęty, zostanie wstrzymany na 4 sekund
	wątek #5 rozpoczęty, zostanie wstrzymany na 4 sekund
	wątek #6 rozpoczęty, zostanie wstrzymany na 3 sekund
	wątek #7 rozpoczęty, zostanie wstrzymany na 4 sekund
	wątek #8 rozpoczęty, zostanie wstrzymany na 4 sekund
	wątek #9 rozpoczęty, zostanie wstrzymany na 4 sekund
wątek główny osiągnął barierę
	wątek #6 osiągnął barierę
	wątek #4 osiągnął barierę
	wątek #5 osiągnął barierę
	wątek #7 osiągnął barierę
	wątek #8 osiągnął barierę
	wszystkie wątki osiągnęły barierę (wątek #9)