Sincronizarea cu semafoarele

Sincronizarea cu semafoarele

În a patra ediție a ghidului populare sunt elementele de bază ale programării în sistemul de operare Linux. Considerată: folosind C C ++ bibliotecă / și sistem instrumente de dezvoltare stan-dard Organization apel, fișier I / O, procesele de interacțiune, programarea mijloacelor shell, creând interfețe grafice cu instrumente GTK + sau Qt, utilizarea prize etc. descrise compilare. programe, legându-le c biblioteci și să lucreze cu terminalul I / O. Sunt tehnici de scriere aplicații în medii GNOME® și KDE®, de stocare a datelor, folosind programe de baze de date MySQL® și depanare. Cartea este bine structurat, ceea ce face de învățare ușor și rapid.

Pentru novice Linux-programatori

Book: Linux Bazele de programare

Sincronizarea cu semafoarele

Secțiunile de pe această pagină:

Sincronizarea cu semafoarele

Pentru semaphore are două seturi de funcții de interfață: una este luată din extensiile POSIX Realtime (supliment POSIX pentru timp real) și aplicate să curgă, iar celălalt, cunoscut sub numele de semafoarelor, System V, este de obicei folosit pentru sincronizarea proceselor. (Vom discuta un al doilea tip în capitolul 14.) Ambele seturi nu garantează interoperabilitatea și, deși foarte asemănătoare, utilizați apeluri la diferite funcții.

Dijkstra, un om de știință olandez de calculator, informatică, formulat mai întâi ideea de a semafoarelor. Semafor - acesta este un tip special de variabilă care poate fi schimbat cu o creștere pozitivă sau negativă, dar referirea la variabila la momentul crucial este întotdeauna chiar și în programele de atomically multithreaded. Acest lucru înseamnă că, în cazul în care două fire (sau mai multe) din programul încearcă să schimbe valoarea semaforului, sistemul asigură că toate operațiunile vor fi efectiv efectuate una după alta. În cazul variabilelor normale ca rezultat al operațiunilor conflictuale ale diferitelor fluxuri într-un singur program este arbitrară.

În această secțiune, considerăm cel mai simplu tip de semafor, binar semaforului sau binare care ia numai valorile 0 și 1. Există o formă mai generalizată a semaforului, luând în considerare (numărare) acceptând o gamă semaforului mai largă de valori. De obicei, semafoarele folosite pentru a proteja software-ul de cod fragment, astfel încât doar un singur fir de execuție se poate schimba în orice moment dat. Acest lucru necesită un semafor binar. Uneori ai nevoie pentru a permite unui număr limitat de fluxuri pentru a efectua o anumită bucată de cod, pentru a face acest lucru ar trebui să utilizați numărare semaphore. Având în vedere că semaforul de numărare este mult mai puțin popular, nu le vom discuta în continuare, spunând doar că acestea sunt o extensie logică a unui semafor binar și că apelurile funcția propriu-zisă trebuie să fie identice.

Numele funcțiilor semafoarelor nu începe cu prefixul pthread_. cele mai multe funcții legate de fluxuri, precum și cu sem_. cele patru funcții de bază ale semafoarelor utilizate pentru streaming. Acestea sunt toate foarte simple.

Semafor este creat de funcția sem_init. este declarată după cum urmează.

#include
int sem_init (sem_t * sem, pshared, valoarea int unsigned int);

Această funcție inițializează obiectul, semaforul indicat de sem. stabilește opțiunea de partajare (pe care le vom discuta într-un minut) și atribuie o valoare întreagă primar. Parametrul Pshared controlează tipul de semafor. Dacă pshared este 0, semaforul este local pentru procesul actual. În caz contrar, semaforul poate fi partajat prin diferite procese. Ne interesează acum semafoarelor care nu sunt partajate între procese. În timpul scrierii cărții a sistemului de operare Linux nu suportă o astfel partajarea și transferul de non-zero parametri de valoare pshared duce la blocarea apelurilor.

Următoarea pereche de funcții și controale ale valorii semaforului este declarată după cum urmează.

Ambele iau un pointer la un obiect, semaforul este inițializat prin apelarea sem_init.

Funcția sem_post atomically crește valoarea semaforului cu 1. atomically, în acest caz, înseamnă că, în cazul în care cele două fire, în același timp, încercând să mărească valoarea unui singur semafor la 1, acestea nu interferează unul cu celălalt, la fel ca în cazul a două programe care citesc și se înregistrează creșterea valorii fișierului în în același timp. În cazul în care ambele programe sunt încercarea de a crește valoarea de 1, semaforul va crește întotdeauna să fie în mod corespunzător valoarea la 2.

Funcția sem_wait atomically scade valoarea semaforului de unul, dar întotdeauna așteaptă până când semaforul până la prima contorul nu primește o valoare non-zero. Astfel, dacă apelați sem_wait semaforul cu o valoare de 2, fluxul va continua să pună în aplicare, iar semaforul va fi redusă la 1. Dacă sem_wait numit un semafor cu o valoare de 0, funcția va aștepta până atunci, până când un alt fir nu va crește valoarea, și va fi non-zero. Dacă ambele fire sunt în așteptare în funcție sem_wait. la aceeași semaforul a devenit non-zero, iar aceasta crește atunci când un al treilea flux, doar unul dintre firele de așteptare vor fi în măsură să reducă semafoarelor și să continue; un alt fir și așa va rămâne în așteptare. Această capacitate a unui „test și set de“ atomic într-o singură funcție și face semaforul atât de valoros.

remarcă

Există o altă caracteristică a semaforului sem_trywait - acesta este non-blocare sem_wait partener. Nu vom discuta într-o carte, în viitor, mai multe informații, consultați. În paginile de manual.

Ultima Funcția semaforului - sem_destroy. Se șterge semaforul când ați terminat cu ea, și se declară după cum urmează:

Din nou, această funcție are un pointer la semafoarelor și curăță orice resurse care ar putea fi. Dacă încercați să distrugă un semafor, care este în așteptare pentru un flux, veți obține o eroare.

La fel ca cele mai multe alte funcții, toate aceste funcții întoarce 0 în caz de succes.

Acum, face exercitiul 12.3.

Exercitiul 12.3. flux semaphore

Textul Thread3.c al acestui program se bazează, de asemenea, pe programul de text thread1.c. Având în vedere că modificările sunt importante, vom prezenta o nouă versiune complet.

#include
#include
#include
#include
#include
#include
void * thread_function (void * arg);
bin_sem sem_t;
WORK_SIZE #define 1024
char work_area [WORK_SIZE];
int main () res int;
pthread_t a_thread;
void * Rezultatul firului;
res = sem_init (bin_sem, 0, 0);
în cazul în care perror ( "Semafor initializare a eșuat") (res = 0!);
ieșire (EXIT_FAILURE);
>
res = pthread_create (a_thread, NULL, thread_function, NULL);
if (! res = 0) perror ( "crearea de fir a eșuat");
ieșire (EXIT_FAILURE);
>
printf ( "intrare un text Enter 'final' la finishn.");
în timp ce (strncmp ( "end", work_area, 3)! = 0) fgets (work_area, WORK_SIZE, stdin);
sem_post (bin_sem);
>
printf ( "nWaiting pentru fir pentru a termina n.");
res = pthread_join (a_thread, thread_result);
în cazul în care (res = 0!) perror ( "fir join a eșuat");
ieșire (EXIT_FAILURE);
>
printf ( "joinedn fir");
sem_destroy (bin_sem);
ieșire (EXIT_SUCCESS);
>
void * Funcția filet (void * arg) în timp ce (! strncmp ( "end", zona de lucru, 3) = 0) printf ( "Scrii charactersn.", strlen (work_area) -1);
sem_wait (bin_sem);
>
pthread_exit (NULL);
>

Prima schimbare importantă - includerea semaphore.h fișier pentru a oferi acces la funcțiile semaforul. În continuare, declarați și un semafor câteva variabile și inițializa semaforul înainte de a crea un nou fir.

bin_sem sem_t;
WORK_SIZE #define 1024
char work_area [WORK_SIZE];
int main () res int;
pthread_t a_thread;
void * thread_result;
res = sem_init (bin_sem, 0, 0);
în cazul în care perror ( "Semafor initializare a eșuat") (res = 0!);
ieșire (EXIT_FAILURE);
>

Rețineți că valoarea inițială a semaforului este 0.

În funcția principală. după ce începe un nou fir, ai citit un text de la tastatură, încărcați spațiul de lucru, și apoi construi contorul folosind sem_post semaforului:

printf ( "intrare un text Enter 'final' la finishn.");
în timp ce (strncmp ( "end", work_area, 3)! = 0) fgets (work_area, WORK_SIZE, stdin);
sem_post (bin_sem);
>

Noul fir pe care sunt în așteptare pentru un semafor și apoi numărul de caractere de intrare:

sem_wait (bin_sem);
în timp ce (! strncmp ( "end", work_area, 3) = 0) printf ( "Tu charactersn de intrare", strlen (work_area) -1);
sem_wait (bin_sem);
>

Până la semaforul este setat, sunt în așteptare pentru intrare de la tastatură. Când obține unele de intrare, se elibereaza semaforul, permitand al doilea fir pentru a număra caracterele înainte de primul fir începe citirea de intrare de la tastatura din nou.

Din nou, fire împărtășesc aceeași matrice work_area. Pentru codul era mai scurt și a fost mai ușor de urmat, am ratat din nou o eroare de verificare, de exemplu, valoarea returnată de funcția sem_wait. Dar, în codul programului de lucru, ar trebui să verificați întotdeauna valorile de retur de eroare în cazul în care nu există nici un motiv suficient pentru a nu verifica.

Se lasă programul să lucreze:

$ Cc -D_REENTRANT thread3.s o- fire -lpthread
$ ./thread3
Introduceți un text. Enter „final“, pentru a termina
Viespea Fabrica
Ai de intrare de 16 caractere
Bănci Iain
Ai de intrare de 10 caractere
capăt
Se așteaptă fir pentru a termina.
Subiect alăturat

Într-un flux program de erori de sincronizare sunt întotdeauna greu de găsit, dar programul pare să fie adaptat și de introducere a textului rapid, și mai pe îndelete pauze.

Când inițializați un semafor, apoi să-l întreb valoarea inițială de 0. Prin urmare, atunci când începe debitului, atunci sem_wait de apel suspendă execuția și așteaptă până când semaforul devine non-zero.

În fluxul principal sunt așteptați atât de mult timp, până când nu va avea un text, iar apoi contorul incrementeaza semaforului utilizând funcția sem_post. care permite imediat un alt fir să se întoarcă de la sem_wait lui funcția și începe să fie difuzate. După ce a numărat caracterele pe care curg din nou cauze sem_wait și suspendă execuția, atâta timp cât firul principal solicită sem_post din nou, pentru a crește semaforul.

defecte non-evidente în dezvoltarea acestui scop ca urmare a unor erori implicite, ușor de dor. Să modifice ușor programul pentru thread3a.c, astfel încât intrarea din text tastatura se înlocuiește temporar textul generat automat. Înlocuiți ciclul de citire, în principal, după cum urmează:

printf ( "intrare un text Enter 'final' la finishn.");
în timp ce (strncmp ( "end", work_area, 3)! = 0) if (strncmp (work_area, "FAST", 4) == 0) sem_post (bin_sem);
strcpy (work_area, "Wheeee.");
> fgets Else (work_area, WORK_SIZE, stdin);
>
sem_post (bin_sem);
>

Acum, dacă introduceți FAST. Programul va permite sem_post. pentru a lansa numărul de caractere, dar actualizează imediat work_area altceva.

$ Cc -D_REENTRANT thread3a.s thread3a -lpthread o-
$ ./thread3a
Introduceți un text. Enter „final“ pentru a termina
Excession
Tu de intrare 9 caractere
FAST
Introduci 7 caractere
Introduci 7 caractere
Introduci 7 caractere
capăt
Se așteaptă fir pentru a termina.
Subiect alăturat

Problema cu acest program este faptul că este de așteptat ca introducerea textului din programul va dura atât de mult timp încât celălalt fir suficient timp pentru a număra caracterele înainte de firul principal să-l trimită să pregătească o nouă porțiune a textului care urmează să fie numărate. Când ai încercat să-l ofere două seturi de cuvinte pentru a număra, în urma rapid reciproc (FAST de la tastatură și apoi Wheeee. Format automat), al doilea flux nu a avut timp pentru a efectua. Dar, pentru a spori semaforul de mai multe ori, deci cred că fluxul a continuat să numere cuvinte și scade valoarea semaforului, atâta timp cât nu devine din nou la zero.

Acest exemplu arată cât de atent trebuie să fie un timp? E condițiile în programele multithreaded. Corectați programul poate fi folosind un semafor suplimentar pentru a obține fluxul principal să aștepte până când consideră că fluxul avut posibilitatea de a termina numărătoarea, dar este mult mai ușor de utilizat un mutex sau exclusiv semaforului, pe care o vom lua în considerare mai jos.