Disaini mustrid (C++): Scoped Lock

Java maailmast tulnuna sain mina Scoped Lock patternist teada, kui olin sunnitud oma vigadest õppima. Loodan, et selle posti läbi lugenud ei pea samu vigu kordama.


Mutexi mõistet ei hakka ma lahti seletama, aga üks võimalus pthread C libile võib wrapper kirjutada näiteks nii:

#include
class Mutex
{
private:
pthread_mutex_t mutex;
 
public:
Mutex(){pthread_mutex_init(&mutex, NULL);}
~Mutex(){pthread_mutex_destroy(&mutex);}
void lock(){pthread_mutex_lock(&mutex);}
void unlock(){pthread_mutex_unlock(&mutex);}
 
private:
Mutex(Mutex const & other);
Mutex const & operator=(Mutex const & other); };

Kui ma ei teaks ScopedLock’ist midagi, siis kasutaks ma Mutexi classi järgnevalt

class Foo
{
private:
Mutex m;
public:
critical_function()
{
m.lock();
 
//protect Foo member variables
//only one thread can acces this section
 
m.unlock();
}
};

Muidu on hea, aga antud kood ei ole exception safe, st. et kui m.lock() ja m.unlock() vahel exception visatakse, siis mutexit lukust lahti ei tehta ja järgmisel threadil ei ole võimalik mutexit enda kätte saada ja seega ei saa ükski thread enam critical_function() välja kutsuda.

Nagu tihti juhtub C++’s juhtub, on lahendus skoobis. Mutex tuleb lukustuda skoobis ja lukust lahti teha skoobi lõppemisel. Üks võimalik ScopedLock implementatsioon:

template  class ScopedLock {
public:
ScopedLock(TLock & l): pLock(&l)
{
l.lock();
}
ScopedLock(TLock * const l): pLock(l)
{
l->lock();
}
~ScopedLock()
{
pLock->unlock();
}
private:
ScopedLock(ScopedLock const & other);
ScopedLock const & operator=(ScopedLock const & other);
TLock * pLock;
};

ehk enne kirjutatud critical_function() näeks välja järgnevalt:

class Foo
{
private:
Mutex m;
public:
critical_function()
{
{
ScopedLock lock(&m); //acquire lock for m
 
/*critical code section
only one thread can acces this section*/
}//unlock m
}
};

Juba parem, aga ikka ei ole päris hea. Erinevalt Javast ei saa sama thread Mutexit mitu korda lockida. Selline olukord tekib näiteks, kui critical_function() läheks rekursiooni. Võimalus on kood nii kirjutada, et rekursiooni ei teki või kasutada recursive_mutexit.

Ok, olen jõudnud siiamaani, kus rohkem sügavuti ma minna ei taha, sest tegelikult on Mutex, RecursiveMutex, ScopeLock jne olemas Boost.Thread libis.

Võrdluseks sama asi Javas:

public class Foo
{
public /*synchronized*/ critical_function()
{
synchronized(this)
{
//only one thread can access this section
}
}
}

Pole vaja muretseda, et locki lahti ei lasta ja kood võib vabalt rekursiooni minna. Muidu on Javas ja C++ multithreaditud programmeerimise probleemid suhteliselt sarnased ja nõuavad mõlemas keeles kõvasti testimist.

Lingid:
ScopedLock ja teised concurrent programmingu patternid

Boost.Thread intro

3 thoughts on “Disaini mustrid (C++): Scoped Lock”

  1. Kalapüügi kõrvalt tahaks kommenteerida, et ajal mil mitmiksüdamikuga protsessorid muutuvad järjest populaarsemaks on sellise pornoga tegelemine ikkagi imelik ja jätab vähem kalapüügiaega progejale.
    Tulevik peab tooma meile keeled kus paralleelsus käib kas automaatselt või lihtsustatult. Ainuke keel mida mina hetkel tean mis midagi sellist reklaamib on Erlang. Aga ega ma kõike ei tea, ootan vastukommentaare.

  2. Et see javas paar rida lühem on ei päästa tegelikult kellegi nahka multithreaded asja olemuslikust mõistmisest. ;) Nii java kui C++ on imperatiivsed keeled … seega peab inimene kes neid kasutab täiega aru saama, mis arvuti tegema hakkab. Keskmine noob ei saa kähku aru pointeristki, rääkimata exceptionist või veel hullem … threadist.

    Enamuste lukkude kasutamine käib umbes nii nagu ma nüüd toon. Jah igasugu lukud pole keele enda osad vaid on standardiseerimata vahendid. Ei taha ikkagi näha, kuidas keegi ise linked liste teeb ja ei rõõmusta ma ka põlve otsas valmistatud sünkroniseerimise üle.

    lock_type l;
    int count;

    void critical_function()
    {
    {
    lock_type::guard g(l);
    if(g) // we did not timeout?
    {
    // critical section things
    ++count;
    }
    else
    throw timeout_exception(); // does java it automatically?
    }
    // unlocked here … we left the scope
    }

Leave a Reply

Your email address will not be published. Required fields are marked *