Back to site
Since 2004, our University project has become the Internet's most widespread web hosting directory. Here we like to talk a lot about web servers, web development, networking and security services. It is, after all, our expertise. To make things better we've launched this science section with the free access to educational resources and important scientific material translated to different languages.

Generička Pravila Sinhronizacije

 


Generička Pravila Sinhronizacije u C++

Ciaran McHale
www.CiaranMcHale.com




Ovaj softver i dokumentacija se distribuiraju pod MIT-style dozvolom (na slici ispod), što u osnovi znači da ne smete da uklonite obaveštenje o autorskim pravima, ali, pored toga, možete da koristite ili menjate ovaj softver i dokumentaciju kako želite. Na primer, možete da ih koristite u projektima otvorenog koda kao i projektima zatvorenog koda, a možete da poklonite softver i dokumentaciju besplatno ili možete da ih prodate. Možete pronaći informacije o licencama otvorenog koda na Open Source Initiative (www.opensource.org).

Autorsko pravo © 2006-2008 Ciaran McHale.

Dozvola je ovime data, besplatno, bilo kojoj osobi koja nabavi kopiju ovog softvera i vezane dokumentacijske datoteke ("Software"), da koriste Software bez ograničenja, kao i bez ograničenja prava da koriste, kopiraju, menjaju, objedinjavaju, objavljuju, distribuiraju, podlicenciraju, i/ili prodaju kopije Software, i ovlašćuje lica za koje je Software opremljen, pod sledećim uslovima:

  • Obaveštenje o autorskim pravima i ovo obaveštenje o dozvoli moraju biti uključeni u svim kopijama ili značajnim delovima Software.
  • SOFTWARE SE DOBIJA "KAKAV JESTE", BEZ GARANCIJA BILO KOJE VRSTE, IZRIČITE ILI PODRAZUMEVANE, UKLjUČUJUĆI, ALI BEZ OGRANIČENjA, GARANCIJE TRGOVINE, POGODNOSTI ZA ODREĐENE POTREBE I BESPRAVNOSTI. U NI U KOM SLUČAJU AUTORI I NOSIOCI AUTORSKIH PRAVA NEĆE BITI ODGOVORNI ZA BILO KOJE POTRAŽIVANjE, ŠTETU ILI DRUGE ODGOVORNOSTI, BILO DA SE TIČE KRŠENjA UGOVORA, DELIKTA ILI NA DRUGI NAČIN, PO OSNOVU, IZ ILI U VEZI SA SOFTVEROM ILI NAMENI ILI DRUGIH RADNjI U SOFTVERU.

1  Introduction

Pisanje koda sinhronizacije je obično teško i ne-prenosivo.

Veći deo teškoća u pisanju koda sinhronizacije je zbog upotrebe niskog nivoa API sinhronizacije.

Problem prenosivosti nastaje jer ni C, ni C++ ne obezbeđuju standardnu biblioteku za sinhronizaciju. Kao rezultat toga, mnogi operativni sistemi obezbeđuju vlasnički API za sinhronizaciju. Neki ljudi kreiraju portability-layer biblioteku koja skriva vlasničke API od osnovnih operativnih sistema. Nažalost, to je uobičajeno za takve biblioteke da bi obezbedile nizak nivo, a samim tim je i teško koristiti API.

Po mom iskustvu, većina upotreba koda za sinhronizaciju u multi-threaded aplikacijama spadaju u mali broj “načina korišćenja” na visokom nivou, ili ono što ja nazivam generičkim pravilima sinhronizacije (GSPs). Ovaj rad pokazuje kako primena takvih GSP-a pojednostavljuje pisanje thread-safe klasa. Pored toga, ovaj rad predstavlja C++ klasnu biblioteku koja implementira najčešće korišćene GSP-ove.

2  Scoped Locks

Threading biblioteke obezbeđuju funkcije koje nabavljaju i oslobađaju blokade uzajamnog isključivanja (mutex). U ovom radu, ja koristim getLock() i releaseLock() za označavanje takvih funkcija. Kritična sekcija je kod koji je stavljen u zagrade sa pozivom na getLock() i releaseLock(). Na primer, sledeće je kritična sekcija:

getLock(mutex);
...
releaseLock(mutex);

Kritična sekcija iznad izgleda jednostavno. Međutim, ako kod u kritičnom delu ima uslovne povratne izjave ili uslovno baca izuzetke onda se mora voditi računa da se oslobodi mutex blokada na svim izlaznim tačkama kritične sekcije. Na Slici  1 prikazan je primer ovoga.


void foo()
{
    getLock(mutex);
    ...
    if (...) {
        releaseLock(mutex);
        return;
    }
    if (...) {
        releaseLock(mutex);
        throw anException;
    }
    ...
    releaseLock(mutex);
}

Figure 1: An operation with a critical section

Generalno, dodavanje poziva na releaseLock(), na svakoj potencijalnoj izlaznoj tački kritične sekcije čini kod neurednim, čime je kod teži za čitanje i održavanje. Zaista, zajednički izvor grešaka u multi-threaded aplikacijama zaboravlja da doda poziv od releaseLock() na nekim od mogućih izlaznih tačaka kritične sekcije.

Postoji korisna tehnika koja uklanja potrebu za pozivima koji prave zbrku i koji su skloni greškama na releaseLock(). Ova tehnika podrazumeva pisanje klase--nazovimo ga ScopedMutex--koja poziva getLock() i releaseLock() u svom konstruktoru i destruktoru. Možete pogledati implementaciju Pseudokoda ove klase na Slici  2.


class ScopedMutex {
public:
    ScopedMutex(Mutex & mutex)
        : m_mutex(mutex)
    {
        getLock(m_mutex);
    }
    ~ScopedMutex() {
        releaseLock(m_mutex);
    }
protected:
    Mutex &    m_mutex;
};

Slika 2: Pseudokod ScopedMutex klasa

Sada, umesto da se eksplicitno poziva getLock() i releaseLock() u telu operacije, možete jednostavno da proglasite ScopedMutex lokalnu promenljivu funkcijom. Slika  3 pokazuje primer ovoga. Kada je funkcija pozvana, konstruktor ScopedMutex promenljive se priziva i to poziva getLock(). Zatim, kada je funkcija završena, destruktor ScopedMutex promenljive se priziva i to poziva releaseLock(). Ovo se dešava bez obzira da li se funkcija vraća rano (linija 1), baca izuzetak (linija 2), ili radi na završetku (linija 3).


void foo()
{
    ScopedMutex    scopedLock(mutex);
    ...
    if (...) return;              // line 1
    if (...) throw anException;   // line 2
    ...
}                                 // line 3

Figure 3: An operation with a scoped mutex lock

Ako uporedite kod na Slikama 13 onda ćete videti da potonji kod (koji koristi ScopedMutex klasu) je kraći i lakši za čitanje od prethodnog.

Ova tehnika korišćenja konstruktor/destruktor klase za sinhronizaciju je delimično poznata u C++ zajednici. Ovo govorim iz dva razloga.

Prvo, moje iskustvo kao konsultanta i trenera mi je dalo priliku da radim sa mnogim C++ programerima u mnogim različitim organizacijama. Našao sam da je oko polovina programera sa kojima radim upoznata sa ovom tehnikom, i da je smatraju osnovnim C++ idiomom, dok je ista tehnika nova drugoj polovini.

Drugo, među programerima koji su koristili ovu konstruktor/destruktor tehniku za sinhronizaciju, upotreba ove tehnike je uvek ograničena na uzajamno isključenje (i povremene readers-writer blokade). Međutim, ova tehnika se može primeniti na druge, složenije sinhronizacije koda takođe (što je i fokus ovog rada).

Pre razgovora o tome kako da primenite ove tehnike u drugim sinhronizacijama koda, potrebno je da malo skrenemo sa puta. Konkretno, moram da uvedem novi koncept: generička pravila sinhronizacije.

3  Generička Pravila Sinhronizacije

C++ podržava tipove šablona. Na primer, spisak klasa može da se napiše kao:

template<T> class List { ... };

Jednom implementiran, ovaj tip šablona može da se instancira više puta da dobije, recimo, spisak int, spisak dvostrukih i spisak Widget:

List<int>       myIntList;
List<double>    myDoubleList;
List<Widget>    myWidgetList;

Sposobnost da se definiše vrsta šablona nije jedinstvena u C++. Nekoliko drugih jezika pružaju sličnu funkcionalnost, mada često postoje razlike u terminologiji i sintaksi. Na primer, neki jezici koriste termin generički tipovi umesto tipovi šablona, a parametri tipa mogu biti zatvoreni u [ and ] umesto < and >.

Koncept genericity nije ograničen na tipove. Može se primeniti na sinhronizaciji takođe, što ću upravo sada diskutovati.

3.1  Mutex i Readers-writer Pravila

Koristeći Pseudokod notaciju, mogu objaviti neka dobro poznata pravila sinhronizacije na sledeći način:

Mutex[Op]
RW[ReadOp, WriteOp]

U ovoj notaciji, prvo je dat naziv generičkog pravila sinhronizacije, a zatim je praćen listom parametara zatvorenom u uglastim zagradama. Svaki parametar označava skup operativnih imena. Na primer, Mutex politika je instancirana na skupu operacija (Op), dok je RW (readers-writer) politika instancirana na skupu read-style operacija (ReadOp) i skupu write-style operacija (WriteOp).

Razmislite o klasi koja ima dve read-style operacije zvane Op1 i Op2, i write-style operaciju zvanu Op3. Ja instanciram RW politiku na ovim operacijama na sledeći način:

RW[{Op1, Op2}, {Op3}]

Isto tako, primer Mutex politike na ove tri operacije je napisan na sledeći način:

Mutex[{Op1, Op2, Op3}]

3.2  Proizvođač-potrošač Politika

Proizvođač-potrošač politika je korisna kada se bafer koristi za prenos podataka sa jednog niza (proizvođač) na drugi niz (potrošač). Proizvođačka nit stavlja stvari u bafer, a zatim, nešto kasnije, potrošačka nit dobija ove stavke. Ako potrošačka nit pokušava da dobije stavku iz praznog bafera onda će biti blokirana sve dok se bafer ne isprazni. Osim toga, put-style i get-style operacije izvršavaju se u međusobnoj isključenosti; a to se radi da bi se sprečilo da bafer postane oštećen usled istovremenog pristupa. Ova politika je napisana na sledeći način:

ProdCons[PutOp, GetOp, OtherOp]

OtherOp označava bilo koje druge (ne put-style i non get-style) operacije na klasi bafera. Na primer, možda postoji operacija na baferu koja vraća brojanje koliko stavki se trenutno nalaze u baferu. Takva operacija će možda morati da se pokrene u međusobnom isključenju sa put-style i get-style operacijama kako bi se osiguralo njeno pravilno funkcionisanje. Ako buffer-style klasa ima operacije pod nazivom insert(), remove() i count() onda možete da instancirate ProdConspolicy na klasu na sledeći način:

ProdCons[{insert}, {remove}, {count}]

Ako klasa nema count() operaciju onda možete da instancirate ProdCons pravilo na tome na sledeći način:

ProdCons[{insert}, {remove}, {}]

U tom slučaju, OtherOp parametar politike je instanciran na praznom skupu operacionih imena.

3.3  Ograničena Proizvođač-potrošač Politika

Zajednička varijacija proizvođač-potrošač politike je ograničena proizvođač-potrošač politika, u kojoj bafer ima fiksnu veličinu. Ovo sprečava da bafer poraste beskonačno veliki, ako jedna nit stavlja stvari u bafer brže nego što druga nit može da ih primi. U ovoj politici, ako producer thread pokuša da stavi stavku u već pun bafer, onda će biti blokiran sve dok se bafer ne napuni. Ova politika je napisana na sledeći način:

BoundedProdCons(int size)[PutOp, GetOp, OtherOp]

Obratite pažnju da je veličina bafera navedena kao parametar u nazivu politike. Takvi parametri su obično instancirani na odgovarajućim parametrima u konstruktoru bafera; primer za to će kasnije biti prikazan (na slici  7).

4  Generička Pravila Sinhronizacije u C ++

TDiskusija u Sekciji 3 fokusirana ja na konceptu GSP. Ja ću sada objasniti kako da primenite GSP u C++.

Slika 4 prikazuje mapiranje Mutex[Op] politike u C++ klasi koristeći POSIX niti. Imajte na umu da, kako bi kod bio koncizan, provere grešaka na povratnim vrednostima POSIX niti biblioteke pozivaju izostavljenje..


 1 class GSP_Mutex {
 1 public:
 3     GSP_Mutex()  { pthread_mutex_init(m_mutex, 0); }
 4     ~GSP_Mutex() { pthread_mutex_destroy(&m_mutex); }
 5  
 6     class Op {
 7     public:
 8         Op(GSP_Mutex &) : m_data(data)
 9               { pthread_mutex_lock(&m_data.m_mutex); }
10         ~Op() { pthread_mutex_unlock(&m_data.m_mutex); }
11     protected:
12         GSP_Mutex &    m_data;
13     };
14  
15     protected:
17         pthread_mutex_t    m_mutex;
16         friend class ::GSP_Mutex::Op;
18 };

Figure 4: Mapping of Mutex[Op] into a C++ class

Mapiranje iz GSP-a u C++ klasi vrši se na sledeći način:

  1. Ime C++ klase je isto kao i naziv GSP, ali sa "GSP_" prefiksom. Prefiks se koristi za sprečavanje zagađenja ime-prostora. Dakle, na Slici  4 Mutex GSP je implementiran od strane GSP_Mutex klase.
  2. Klasa ima jednu ili više varijabli instance (red 17) koje obezbeđuju prostor mutex. Konstruktor i destruktor klase (redovi 3 i 4) pokreću i uništavaju varijable instance(s).
  3. Mutex[Op] GSP ima parametar koji se naziva Op. To se prenosi u umetnutu klasu (redovi 6-13) sa istim imenom. Ako GSP ima nekoliko parametara onda se svaki parametar prevodi u posebnu umetnutu klasu; primer za to će kasnije biti prikazan.
  4. Svaka umetnuta klasa ima jednu promenljivu instance (red 12), što je referenca na spoljašnju klasu. Ova varijabla instance je pokrenuta od parametra do konstruktora unutrašnje klase (red 8).
  5. Konstruktor i destruktor umetnute ​​klase dobijaju i otpuštaju blokadu (redovi 9 i 10) koji se nalazi u instanci spoljašne klase.

Drugi primer, Slika 5 prikazuje kako se RW[ReadOp, WriteOp] GSP mapira u C++ klasi. Obratite pažnju na to da zato što ovaj GSP koristi dva parametra, postoje dve umtenute klase.


class GSP_RW {
public:
    GSP_RW()  { /* initialize the readers-writer lock */ }
    ~GSP_RW() { /* destroy the readers-writer lock */ }

    class ReadOp {
    public:
        ReadOp(GSP_RW & data) : m_data(data)
                     { /* acquire read lock */ }
        ~ReadOp()    { /* release read lock */ }
    protected:
        GSP_RW &    m_data;
    };

    class WriteOp {
    public:
        WriteOp(GSP_RW & data) : m_data(data)
                     { /* acquire write lock */ }
        ~WriteOp()   { /* release write lock */ }
    protected:
        GSP_RW &    m_data;
    };

protected:
    ...  // Instance variables required to implement a
         // readers-writer lock
    friend class ::GSP_RW::ReadOp;
    friend class ::GSP_RW::WriteOp;
};

Slika 5: GSP_RW klasa sa umetnutom ​​ReadOp i WriteOp klasama

Instanciranje GSP na operacijama C++ klase uključuje sledeća tri koraka:

  1. # uključuju datoteku zaglavlja za GSP. Naziv datoteke zaglavlja je isti kao i ime GSP klase, ali je napisan malim slovima. Na primer, zaglavlje datoteke za GSP_RW klasu je "gsp_rw.h".
  2. Dodajte promenljivu instance C++ klasi koja treba da se sinhronizuje. Instanca tipa varijable je ona od spoljašnje klase GSP-a.
  3. Unutar tela svake operacije koja treba da se sinhronizuje, proglasiti lokalnu promenljivu, tip umetnute klase GSP.

Instaliranje RW[{Op1, Op2}, {Op3}] na slici 6 ilustruje ove korake.


#include "gsp_rw.h"
class Foo {
public:
    Foo()  { ... }
    ~Foo() { ... }
    void Op1(...) {
        GSP_RW::ReadOp     scopedLock(m_sync);
        ... // normal body of operation
    }
    void Op2(...) {
        GSP_RW::ReadOp     scopedLock(m_sync);
        ... // normal body of operation
    }
    void Op3(...) {
        GSP_RW::WriteOp    scopedLock(m_sync);
        ... // normal body of operation
    }
protected:
    GSP_RW    m_sync;
    ... // normal instance variables of class
};

Slika 6: Instanciranje GSP_RW

Kao poslednji primer, Slika 7 pokazuje klasu koja se instancira sa:

BoundedProdCons(int size)[PutOp, GetOp, OtherOp]

Ova politika koristi parametar koji ukazuje na veličinu bafera. Ovaj parametar se dobija iz bufSize parametra konstruktora klase.


#include "gsp_boundedprodcons.h"
class WidgetBuffer {
public:
    WidgetBuffer(int bufSize) : m_sync(bufSize)  { ... }
    ~WidgetBuffer()                             { ... }

    void insert(Widget * item) {
        GSP_BoundedProdCons::PutOp scoped_lock(m_sync);
        ... // normal body of operation
    }

    Widget * remove() {
        GSP_BoundedProdCons::GetOp scoped_lock(m_sync);
        ... // normal body of operation
    }
protected:
    GSP_BoundedProdCons    m_sync;
    ... // normal instance variables of class
};

Figure 7: Instantiation of GSP_BoundedProdCons

5  Podrška za Generička Pravila Sinhronizacije na Drugim Jezicima

Implementacija GSP-ova prikazana u ovom radu oslanja se na to da konstruktori i destruktori automatizuju izvršavanje sinhronizacije koda. Iako objektno-orijentisani jezici obično obezbeđuju konstruktore, ne pružaju svi objektno-orijentisani jezici destruktore, posebno jezici koje imaju ugrađene kolektore smeća. To može dovesti čitaoce na zaključak da se GSP ne mogu implementirati u postojećim jezicima koji ne pružaju destruktore. Iako ovo može biti tačno, bilo bi moguće za dizajnere budućih jezika da ugrade podršku za GSP u njihov dizajn jezika. Na primer, u mojoj doktorskoj tezi [] ja pokazujem kako da dodate podršku za GSP, kompajleru objektno-orijentisanog jezika koji koristi kolektore smeća umesto destruktora.

6  Kritika Generičkih Pravila Sinhronizacije

Sada ću ukazati na neke prednosti i moguće mane GSP.

6.1  Prednosti GSP

Prvo, GSP-ovi pružaju dobar oblik ponovne upotrebe veština. Konkretno, mnogo je lakše korišćenje GSP-a nego njegova implementacija. Tako da, programer vešt u sinhronizacionom programiranju može implementirati šta god je GSP-u potrebno za projekat, a zatim drugi, manje vešti, programeri mogu da koriste ove unapred napisane GSP-ove.

Drugo, GSP-ovi potpomažu kodnu čitljivost i održavanje izdvajanjem sinhronizacionog koda iz "normalnog ", funkcionalnog koda klase.

Treće, kao što sam u Poglavlju 2, već diskutovao, postavljanjem koda sinhronizacije u konstruktore i destruktore GSP klasa znači da su blokade puštene čak i ako se operacija završi ranijim vraćanjem ili bacanjem izuzetaka. Ovo eliminiše zajednički izvor grešaka u multi-threaded programima.

Četvrto, GSP obezbeđuju ne samo jednostavnost upotrebe; oni takođe pružaju sloj prenosivosti oko osnovnih sinhronizacionih primitiva. Naravno, neke kompanije su razvili kućne, biblioteke prenosivosti koje kriju razlike između sinhronizacionih primitiva na različitim platformama, a neke druge kompanije koriste druge biblioteke prenosivosti, kao što su Threads.h++ biblioteka iz RogueWave. Upotreba GSP-ova je kompatibilna sa postojećim bibliotekama: GSP-ovi se mogu implementirati veoma lako povrh Threads.h++ (ili na nekoj drugoj biblioteci prenosivosti), jer se mogu implementirati direktno na vrhu operativnog sistema specifičnih sinhronizacionih primitiva.

I na kraju, implementacija GSP može biti postrojena. Dakle, upotrebu GSP-ova ne treba nametati iznad performansi.

6.2  Potencijalne Kritike GSP

Neki čitaoci možda misle: "GSP-ovi su ograničeni; oni ne mogu kontrolisati sve sinhronizacione potrebe koje možda imam. "Međutim, u mnogim aktivnostima, neproporcionalno veliki iznos rezultata dolazi iz relativno malog obima investicija. Ovo je opšte poznato kao 80/20 princip []. Po mom iskustvu, to se odnosi na sinhronizacione zahteve aplikacija. Mali skup GSP je verovatno dovoljan za većinu sinhronizacionih potreba programera. Dakle, čak iako mali skup unapred napisanih GSP ne može da kontroliše sve sinhronizacione potrebe sa kojima će se programer suočiti, 80/20 princip sugeriše da bi upotreba GSP-ova bila od koristi dovoljno često da bi opravdali svoju upotrebu.

Naravno, ljudi nisu ograničeni samo na mali skup unapred napisanih GSP. Ljudi mogu da definišu nove GSP-ove. Na primer, možda programer treba da napiše neki projektno-specifični kod sinhronizacije. Čak iako će taj sinhronizacioni kod biti korišćen na samo jednom mestu u projektu, teško da će bilo koji dodatni posao da implementira ovo kao GSP, a zatim da ga instancira, umesto da ga implementira "in-line" u operacijama klase. Radeći to nudi nekoliko prednosti:

  1. Realizacija sinhronizacije koda kao da će GSP verovatno poboljšati čitljivost i održavanje sinhronizacionog koda i sekvencijalnog koda projekta.
  2. Ako programer kasnije otkrije još jedno mesto koje treba da koristi istu politiku onda se GSP može ponovo koristiti direktno, umesto da ponovo koristite in-lined kod preko copy-n-paste.

Neki drugi čitaoci možda misle: "GSP-ovi nisu novi; ‘GSP’ je samo novo ime za postojeći C++ idiom". Tvrdnja da se GSP-ovi zasnivaju na već poznatom C++ idiomu (ScopedMutex klasa razmatrana u Poglavlju  2) je potpuno tačna. Zaista, ScopedMutex klasa je GSP u svemu osim u imenu. Međutim, kako je navedeno u Poglavlju  2, C++ idiom koji je čini osnovu GSP-ova je ranije korišćen samo za mutex i povremeno za readers-writer politiku. Značajan doprinos GSP-ova ističe da ista tehnika može da se koristi za većinu, ako ne i sve, sinhronizacione politike.

7  Pitanja kojima se ne bave GSP-ovi

GSP-ovi ilustruju 80/20 princip: većina sinhronizacionih zahteva programera može biti zadovoljena malim delom GSP-ova. Međutim, postoje neka pitanja sinhronizacije koja nisu rešavana od strane GSP-ova. Sada ću kratko raspravljati o ovim pitanjima u nastavku, tako da čitaoci mogu biti upozoreni o tome kada upotreba GSP-ova nije pogodna.

7.1  Otkazivanja Niti

Specifikacija POSIX niti obezbeđuje mehanizam za nit da bude poništena, to jest, prekinuta graciozno. Kada je nit otkazan, važno je da nit ima priliku da uradi malo sređivanja pre nego što bude prekinuta, na primer, nit možda želi da oslobodi blokade koje je posedovala. To se postiže tako što programer registruje callback funkcije koje će biti pozvane u slučaju da nit bude otkazana. Trenutna implementacija GSP-ova ne pruža podršku za sposobnost otkazivanja POSIX niti. To nije zbog unutrašnje nekompatibilnosti između GSP-ova i otkazivanja niti. Već jednostavno zbog toga što autoru nikada nije bilo potrebno da iskoristi otkazivanje niti. Integrisanje GSP-ova sa otkazivanjem niti je ostalo kao vežba zainteresovanim čitaocima.

7.2  Vremenska ograničenja

Neki paketi niti pružaju mogućnost čekanja sinhronizacionih primitiva. Koristeći ovo, programer može odrediti gornju granicu vremena koliko dugo treba nit da čeka da bi, na primer, dobila blokadu. Trenutna implementacija GSP-ova ne pruža mogućnost tajm-auta. Postoje dva razloga za to.

Prvo, tajm-auti su retko potrebni, a samim tim, praćenjem 80/20 principa, odlučio sam da ih ne podržavam.

Drugo, implementacija mogućnosti tajm-auta je relativno složena sa API nekih paketa niti. Na primer, Mutex politika bez mogućnosti tajm-auta može biti trivijalno implementirana u POSIX nitima pozivanjem funkcija na osnovni tip mutex-a. Nasuprot tome, sprovođenje Mutex politike sa mogućnošću tajm-auta u POSIX nitima zahteva korišćenje mutex promenljive i promenljive stanja; rezultujući algoritam je složeniji za pisanje i održavanje, a njime stvaraju najmanje dva puta više performansi nego kao Mutex bez tajm-aut sposobnosti. Ovaj dodatni učinak iznad sugeriše da ako neki programeri odluče da zahtevaju Mutex politiku sa mogućnošću tajm-auta onda bi oni trebalo da ga implementiraju kao novi GSP, recimo, TimeoutMutex, radije nego da dodajemo tajm-aut mogućnost postojećoj Mutex politici. Na ovaj način, programeri mogu da koriste politiku TimeoutMutex u nekim prilikama kada im to treba, a mogu da koristi efikasniju Mutex politiku u svim drugim prilikama.

7.3  Hijerarhija Blokade

GSP-ovi su korisni za klase ili objekte koji imaju samostalnu sinhronizaciju. Međutim, ponekad zahtevi sinhronizacije od nekoliko klasa su tesno povezani, a programer treba da obezbedi blokove na dva (ili više) objekata pre izvršenja zadatka. Potreba da se obezbede blokovi na više objekata u isto vreme se obično naziva blok hijerarhija. Pokušaj da se obezbedi hijerarhija blokade može dovesti do zastoja ako se radi nepravilno. Algoritmi za sticanje hijerarhije blokade sigurni su izvan okvira ovog rada, ali se mogu naći na drugom mestu []. Stvar je da algoritmi za sticanje hijerarhije blokade bezbedno zahtevaju neometan pristup blokovima primitiva. To je u suprotnosti sa GSP-ovima, koji potpuno obuhvataju osnovne primitive sinhronizacije.

8  GSP Biblioteka Klasa

Ovaj rad prati biblioteku GSP klasa. Možete preuzeti ovaj rad, i biblioteku sa www.CiaranMcHale.com/download. Biblioteka sprovodi sve GSP-ove razmatrane u ovom radu, odnosno, GSP_Mutex, GSP_RW, GSP_ProdCons i GSP_BoundedProdCons. Biblioteka sprovodi ove GSP-ove za sledeće pakete niti: Solaris threads, DCE Threads, POSIX threads and Windows threads. Imitacije implementacija ovih GSP-ova za non-threaded sisteme su takođe obezbeđene; a to omogućava da se napiše klasa koja se može koristiti i u sekvencijalnim i u multi-threaded aplikacijama.

Sve GSP klase se implementiraju sa inline kodom u datotekama zaglavlja. Zbog toga, da bi koristili GSP potrebno je da samo #uključite odgovarajuću datoteku zaglavlja; ne postoji GSP biblioteka za povezivanje na vašu aplikaciju. Naziv datoteke zaglavlja za GSP je ista kao i ime GSP klase, ali se pišu sa malim slovima. Na primer, datoteka zaglavlja za the GSP_RW klasu je "gsp_rw.h".

Trebalo bi da koristite opciju -D<symbol_name> na vašem C++ kompajleru da definišete jedan od sledećih simbola:

  • P_USE_WIN32_THREADS
  • P_USE_POSIX_THREADS
  • P_USE_DCE_THREADS
  • P_USE_SOLARIS_THREADS
  • P_USE_NO_THREADS

Simbol nalaže GSP klasi koji osnovni paket niti bi trebalo da koristi.

9  Zahvalnost

Koncept GSP-ova ima svoje korene u mojoj doktorskoj tezi []. Istraživanje za ovaj doktorat delimično je finansiran od strane Comandos ESPRIT projekta i izvršen je dok sam bio član Distributed Systems Group (DSG) u Department of Computer Science, Trinity College, Dublin, Ireland. Želim da se zahvalim DSG na njihovoj podršci. Takođe želim da se zahvalim kolegama u IONA Technologies na njihovim komentarima za ranije verzije ovog rada.

10  O autoru

Ciaran McHale radi za IONA Technologies (www.iona.com), koja je specijalizovana u zasnovanoj na standardima posredničkoj distribuciji. On tu radi od 1995, i glavni je konsultant. On ima doktorat u informatici na Trinity College, Dublin, Ireland. Zainteresovani čitaoci mogu pronaći više informacija o Ciaran McHale na njegovom ličnom veb-sajtu: www.CiaranMcHale.com.


Published (Last edited): 08-05-2013 , source: http://www.ciaranmchale.com/gsp/