Folosind mutex și semafor

Folosind mutex și semafor

Cartea este concepută pentru o gamă largă de cititori interesați de programarea în C # Introduceți aici o scurtă adnotare

Carte: C # 4.0: Ghidul complet

Folosind mutex și semafor

Secțiunile de pe această pagină sunt:

Folosind mutex și semafor







În majoritatea cazurilor, când este necesară sincronizarea, este suficientă și blocarea operatorului. Cu toate acestea, în anumite cazuri, cum ar fi restricționarea accesului la resurse partajate, mecanismele de sincronizare integrate în .NET Framework se dovedesc mai convenabile. Mai jos, sunt luate în considerare două astfel de mecanisme: un mutex și un semafor.

Un mutex este un obiect de sincronizare care se exclud reciproc. Aceasta înseamnă că acesta poate fi primit numai de către flux pe rând. Mutexul este conceput pentru situațiile în care o resursă partajată poate fi utilizată simultan într-un singur fir. Să presupunem că jurnalul de sistem este împărțit în mai multe procese, dar numai într-unul din ele datele pot fi scrise în fișierul jurnal la un moment dat. Pentru a sincroniza procesele în această situație, mutexul este ideal.

Mutexul este susținut în clasa System.Threading.Mutex. Are mai mulți designeri. Mai jos sunt cei doi constructori cei mai comuni.

public Mutex ()
public Mutex (bool initiallyOwned)

Prima formă a constructorului creează un mutex, pe care nimeni nu îl deține inițial. Și în cea de-a doua formă, starea inițială a mutexului este capturată de firul de asteptare dacă parametrul InitialOwned are o valoare booleană adevărată. În caz contrar, nimeni nu deține mutexul.

Pentru a obține un mutex, în codul programului, apelați metoda WaitOne () pentru acest mutex. Metoda WaitOne () este moștenită de clasa Mutex din clasa Thread.WaitHandle. Mai jos este forma sa cea mai simplă.

Metoda WaitOne () așteaptă până când este recuperat mutexul pentru care a fost apelat. Prin urmare, această metodă blochează executarea firului de asteptare până când mutexul specificat este disponibil. Întoarce întotdeauna o valoare booleană a adevărului.

Atunci când codul nu mai are nevoie să dețină un mutex, este eliberat apelând metoda ReleaseMutex (). a cărui formă este dată mai jos.

public void ReleaseMutex ()

În această formă, metoda ReleaseMutex () eliberează mutexul pentru care a fost numit, ceea ce permite unui alt fir să primească mutexul dat.

Pentru a utiliza un mutex pentru sincronizarea accesului la o resursă partajată, metodele WaitOne () și ReleaseMutex () menționate mai sus sunt utilizate așa cum se arată în fragmentul de cod de mai jos.

Mutex myMtx = Mutex nou ();
//.
myMtx.WaitOne (); // așteptați să primiți un mutex
// Accesați resursele partajate.
myMtx.ReleaseMutex (); // eliberați mutexul

Când metoda WaitOne () este apelată, firul corespunzător este suspendat până la primirea unui mutex. Și atunci când este apelată metoda ReleaseMutex (), mutexul este eliberat și poate fi apoi recuperat de alt fir. Datorită acestei abordări a sincronizării, accesul simultan la o resursă partajată este limitat la un singur fir.

În exemplul de mai jos, mecanismul de sincronizare descris mai sus este demonstrat în practică. În acest program, două fire sunt create sub formă de clase IncThread și DecThread. care necesită acces la resursa partajată: variabila SharedRes.Count. În fluxul IncThread, variabila SharedRes.Count este incrementată, iar în fluxul DecThread este diminuată. Pentru a evita accesul simultan al ambelor fire la resursa partajată SharedRes.Count, acest acces este sincronizat cu mutexul Mtx. de asemenea, un membru al clasei SharedRes.

Acest program dă următorul rezultat.

Un thread incremental așteaptă un mutex.
Un fir de incrementare primește un mutex.
Decrementarea fluxului se așteaptă la un mutex.
În flux, fluxul incremental, SharedRes.Count = 1






În flux, fluxul incremental, SharedRes.Count = 2
În flux, Incremental Flow, SharedRes.Count = 3
În flux, Incremental Flow, SharedRes.Count = 4
În flux, fluxul incremental, SharedRes.Count = 5
Un fir incrementant eliberează mutexul.
Firul de decrementare primește un mutex.
În fluxul fluxului de degradare, SharedRes.Count = 4
În fluxul fluxului de degradare, SharedRes.Count = 3
În fluxul Decrementing Flow, SharedRes.Count = 2
În fluxul Decrementing Flow, SharedRes.Count = 1
În Stream Decrementing Flow, SharedRes.Count = 0
Decrementarea fluxului eliberează mutexul.

După cum rezultă din rezultatul de mai sus, accesul la resursa partajată (variabila SharedRes.Count) este sincronizat și, prin urmare, valoarea acestei variabile poate fi schimbată simultan într-un singur fir.

Un thread incremental așteaptă un mutex.
Un fir de incrementare primește un mutex.
Decrementarea fluxului se așteaptă la un mutex.
Firul de decrementare primește un mutex.
În flux, fluxul incremental, SharedRes.Count = 1
În Stream Decrementing Flow, SharedRes.Count = 0
În flux, fluxul incremental, SharedRes.Count = 1
În Stream Decrementing Flow, SharedRes.Count = 0
În flux, fluxul incremental, SharedRes.Count = 1
În Stream Decrementing Flow, SharedRes.Count = 0
În flux, fluxul incremental, SharedRes.Count = 1
În Stream Decrementing Flow, SharedRes.Count = 0
În flux, fluxul incremental, SharedRes.Count = 1
Un fir incrementant eliberează mutexul.
În Stream Decrementing Flow, SharedRes.Count = 0
Decrementarea fluxului eliberează mutexul.

După cum rezultă din rezultatul de mai sus, fără creșterea și decrementarea mutexului, variabila SharedRes.Count apare mai aleator decât secvențial.

Mutexul creat în exemplul precedent este cunoscut numai procesului care a dat naștere. Dar un mutex poate fi creat și în așa fel încât să fie cunoscut în altă parte. Pentru aceasta, trebuie să fie numit. Mai jos sunt formele constructorului concepute pentru a crea un astfel de mutex.

public Mutex (bool initiallyOwned, numele de sir)
public Mutex (bool initiallyOwned, nume sir, out bool creatNew)

În ambele forme ale constructorului, numele denotă numele specific al mutexului. În prima formă a constructorului, parametrul InitialOwned are o valoare booleană a valorii adevărate. atunci este cerut mutexul. Dar, deoarece mutexul poate aparține unui alt proces la nivel de sistem, este mai bine să specificați o valoare logică a parametrului false pentru acest parametru. Și după revenirea din cea de-a doua formă a constructorului, parametrul createdNew va avea o valoare booleană a adevăratului. dacă proprietatea mutex a fost solicitată și primită și valoarea logică este falsă. dacă cererea de posesie a fost respinsă. Există și oa treia formă a constructorului de tip Mutex. în care este permisă specificarea unui obiect de control al accesului de tip MutexSecurity. Cu ajutorul mutexelor numite, puteți sincroniza interacțiunea proceselor.

Ultima notă: în firul care a primit mutexul, este permisă efectuarea unuia sau mai multor apeluri suplimentare la metoda WaitOne () înainte de a apela metoda ReleaseMutex (). toate aceste apeluri suplimentare vor avea succes. Aceasta înseamnă că apelurile suplimentare la metoda WaitOne () nu vor bloca firul care deține deja mutexul. Dar numărul de apeluri la metoda WaitOne () trebuie să fie egal cu numărul de apeluri la metoda ReleaseMutex () înainte de a elibera mutex-ul.

Un semafor este similar cu un mutex, cu excepția faptului că oferă acces simultan la o resursă partajată nu la una, ci la mai multe fire. Prin urmare, semaforul este potrivit pentru sincronizarea unui număr de resurse. Un semafor controlează accesul la o resursă partajată folosind un contor pentru acest scop. Dacă valoarea contorului este mai mare decât zero, este permis accesul la resursă. Și dacă această valoare este zero, atunci accesul la resursă este interzis. Cu ajutorul contorului, se ia în considerare numărul de permisiuni. Prin urmare, pentru a accesa resursa, fluxul trebuie să obțină permisiunea din semafor.

De obicei, un fir care are nevoie de acces la o resursă partajată încearcă să obțină permisiunea de la semafor. Dacă valoarea contorului semaforului este mai mare decât zero, fluxul primește permisiunea și contorul semaforului este diminuat. În caz contrar, firul este blocat până când acesta primește permisiunea. Atunci când firul nu mai are nevoie de acces la resursa partajată, acesta eliberează permisiunea și contorul semaforului este incrementat. Dacă permisiunile așteaptă un alt fir, atunci îl primește în acel moment. Numărul de accesuri concurente este specificat la crearea unui semafor. Deci, dacă creați un semafor care permite simultan un singur acces, atunci un astfel de semafor va acționa ca un mutex.

Semaphorele sunt deosebit de utile atunci când o resursă partajată constă într-o grupă de grupuri de resurse. De exemplu, un grup de resurse poate consta dintr-un număr de conexiuni de rețea, fiecare din acestea servind la transferul de date. Prin urmare, firul care are nevoie de o conexiune de rețea nu are grijă de conexiunea pe care o va primi. În acest caz, semaforul oferă un mecanism convenabil pentru controlul accesului la conexiunile de rețea.

Semaforul este implementat în clasa System.Threading.Semaphore. care are mai mulți designeri. Mai jos este cea mai simplă formă a constructorului din această clasă:

semaforul public (int initialCount, int maximumCount)

unde initialCount este valoarea inițială pentru numărul permisiunilor semaforului, adică numărul de autorizații inițiale; maximumCount - valoarea maximă a acestui numărător, adică Numărul maxim de permisiuni pe care le poate oferi un semafor.

Semaforul este folosit în același mod ca mutexul descris anterior. Pentru a accesa resursa în codul programului, se apelează metoda WaitOne () pentru semafor. Această metodă este moștenită de clasa Semaphore din clasa WaitHandle. Metoda WaitOne () așteaptă până când este recuperat semaforul pentru care se numește. Astfel, blochează executarea firului de asteptare până când semaforul specificat dă permisiunea de a accesa resursa.







Articole similare

Trimiteți-le prietenilor: