PredchádzajúciHoreDomovNasledujúci

Synchronizácia - mutexy, pthread_mutex

V prípade, že vo viacerých vláknach bežiacich súčasne používame rovnaké prostriedky OS - pridelenú pamäť (vrátane spoločných premenných), súbory, atď. je potrebné prístup k nim synchronizovať - t.j. zabezpečiť aby daný prostriedok v danom čase nemodifikovalo viac ako jedno vlákno.

Medzi základné prostriedky na synchronizáciu implementované knižnicou pthreads patria mutexy. Mutex je synchronizačný prostriedok, ktorý pri správnom používaní zabezpečuje to, že určitú časť kódu (takú kde sa pracuje so zdieľaným prostriedkom) v danom čase vykonáva len jedno vlákno. Ak by iné vlákna chceli pristupovať k tomu istému prostriedku musia čakať.

Mutex je objekt, ktorý môže byť v dvoch stavoch:

a sú nad ním implementované dve atomické operácie (také, že sa vykonajú ako celok bez prerušenia):

Mutexy sa najčastejšie používajú tak, že ku každému samostatnému zdieľanému prostriedku je pridelený jeden mutex. Ak chce vlákno vykonávať akýkoľvek kód, ktorý manipuluje so zdieľaným prostriedkom, musí na mutexe, ktorý je pridelený k tomuto prostriedku zavolať operáciu lock. Akonáhle vlákno dokončí manipuláciu so zdieľaným prostriedkom, musí zavolať na príslušnom mutexe operáciu unlock.

V knižnici pthreads sú mutexy reprezentované typom pthread_mutex_t.

Pred použitím mutexu je nutné ho inicializovať pomocou funkcie, pthread_mutex_init a keď už nie je potrebný upratať ho pomocou funkcie pthread_mutex_destroy.

Funkcia pthread_mutex_lock slúži na zamknutie a funkcia pthread_mutex_unlock na odomknutie mutexu.

Cvičenie V nasledujúcom príklade si vyskúšame synchronizovaný prístup k štandardnému výstupu z viacerých vlákien, ktoré naň budú paralelne zapisovať. Nechceme však aby výstup jednovlivých vlákien bol "pomiešaný", chceme aby vlákna vypísali vždy celé riadky.

Vytvorte zdrojový súbor pthread_mutex.c s nasledovným obsahom:

#include <pthread.h>
#include <sys/ioctl.h>
#include <stdio.h>

struct thread_data 1
{
	unsigned columns;
	unsigned lines;
	char output;

	pthread_mutex_t *stdout_mutex;
};

void* thread_main(void* ptr)
{
	struct thread_data* data = (struct thread_data*)ptr;

	unsigned l, c;

	for(l=0; l<data->lines; ++l)
	{
		pthread_mutex_lock(data->stdout_mutex); 2

		for(c=0; c<data->columns; ++c)
		{
			fputc(data->output, stdout);
		}
		fputc('\n', stdout);

		pthread_mutex_unlock(data->stdout_mutex); 3
	}

	return NULL;
}

unsigned get_term_width(void) 4
{
	struct winsize ws;
	ioctl(0, TIOCGWINSZ, &ws);

	return ws.ws_col;
}

int main(void)
{
	unsigned w = get_term_width();

	pthread_mutex_t stdout_mutex;
	pthread_mutex_init(&stdout_mutex, NULL); 5
	
	struct thread_data data[4] = {
		{w, 1000, 'X', &stdout_mutex},
		{w, 1200, 'O', &stdout_mutex},
		{w, 1500, 'I', &stdout_mutex},
		{w, 1500, '.', &stdout_mutex}
	}; 6

	pthread_t threads[4];

	int i;
	for(i=0; i<4; ++i)
	{
		pthread_create(&threads[i], NULL, &thread_main, &data[i]); 7
	}

	for(i=0; i<4; ++i)
	{
		pthread_join(threads[i], NULL); 8
	}

	pthread_mutex_destroy(&stdout_mutex); 9
	return 0;
}

1

Štruktúra, ktorá riadi výstup jednotlivých vlákien:

  • columns je šírka terminálu (počet znakov na riadku).
  • lines je počet riadkov ktoré má vlákno vypísať.
  • output je výstupný znak.
  • stdout_mutex je ukazovateľ na mutex, ktorý riadi prístup k štandardnému výstupu.

2

V tomto programe je zdieľaným prostriedkom štandardný výstup. Ak chce niektoré z vlákien štandardný výstup používať, a vypísať celý riadok bez prerušenia inými vláknami, musí k nemu mať výhradný prístup. To zabezpečíme tak, že pred tým ako začneme vypisovať riadok, zamkneme mutex, ktorý riadi prístup k stdout.

3

Akonáhle vypíšeme celý riadok, mutex odomkneme a tým dáme šancu jednému z ostatných vlákien čakajúcich na mutexe, na to aby mutex zamkli a pokračovali vo vykonávaní.

4

Vráti šírku terminálu.

5

Vytvoríme a zinicializujeme mutex.

6

Vytvoríme dáta pre štyri vlákna, prvé bude vypisovať 1000 riadkov pozostávajúcich zo znaku X, druhé 1200 riadkov znaku O, atď. Všetky majú ukazovateľ na spoločný mutex.

7

Vytvoríme a spustíme štyri vlákna, pričom každému dáme ukazovateľ na vlastnú inštanciu dát.

8

Počkáme na dokončenie všetkých spustených vlákien.

9

Mutex už nebudeme potrebovať, takže ho môžme "upratať".

Cvičenie Upravte súbor Makefile:

OUTPUTS = ... pthread_mutex

Skompilujte a spustite program pthread_mutex:

$> make pthread_mutex
$> ./pthread_mutex

PredchádzajúciHoreDomovNasledujúci