2012-08-22 9 views
8

Stavo controllando la libreria boost (versione 1.45) per un blocco lettore/scrittore. Quando ho eseguito i miei test su di esso, sembrava che il parametro shared_ptr preferisse i thread del mio lettore, cioè quando il mio writer ha provato a prendere il lock per la sua operazione, non ha impedito l'esecuzione di successive letture.Boost shared_lock. Leggere preferito?

E 'possibile in boost modificare questo comportamento?

using namespace std; 
using namespace boost; 

mutex outLock; 
shared_mutex workerAccess; 
bool shouldIWork = true; 

class WorkerKiller 
{ 
public: 

    void operator()() 
    { 
     upgrade_lock<shared_mutex> lock(workerAccess); 
     upgrade_to_unique_lock<shared_mutex> uniqueLock(lock); 

     cout << "Grabbed exclusive lock, killing system" << endl; 
     sleep(2); 
     shouldIWork = false; 
     cout << "KILLING ALL WORK" << endl; 
    } 

private: 
}; 

class Worker 
{ 
public: 

    Worker() 
    { 
    } 

    void operator()() 
    { 
     shared_lock<shared_mutex> lock(workerAccess); 

     if (!shouldIWork) { 
      outLock.lock(); 
      cout << "Workers are on strike. This worker refuses to work" << endl; 
      outLock.unlock(); 
     } else { 
      sleep(1); 

      outLock.lock(); 
      cout << "Worked finished her work" << endl; 
      outLock.unlock(); 
     } 
    } 
}; 

int main(int argc, char* argv[]) 
{ 
    Worker w1; 
    Worker w2; 
    Worker w3; 
    Worker w4; 
    WorkerKiller wk; 

    boost::thread workerThread1(w1); 
    boost::thread workerThread2(w2); 

    boost::thread workerKillerThread(wk); 

    boost::thread workerThread3(w3); 
    boost::thread workerThread4(w4); 

    workerThread1.join(); 
    workerThread2.join(); 
    workerKillerThread.join(); 
    workerThread3.join(); 

    return 0; 
} 

Ed ecco l'output ogni volta:

lavorato finito il suo lavoro
lavorato finito il suo lavoro
lavorato finito il suo lavoro
lavorato finito il suo lavoro
Afferrato blocco esclusivo, uccidendo system
KILLING ALL WORK

mia esigenza

Se lo scrittore ha cercato di afferrare un blocco esclusivo, vorrei per tutte le operazioni di lettura precedenti per finire. E poi tutte le successive operazioni di lettura da bloccare.

+2

Si potrebbe voler inviare un po 'di codice di prova - le mie capacità di debug psichici sono :-) deboli – Bukes

risposta

19

Sono un po 'in ritardo a questa domanda, ma credo di avere alcune informazioni pertinenti.

Le proposte di shared_mutex al comitato C++, su cui si basano le librerie di boost, non hanno specificato un'API per dare priorità ai lettori o ai writer. Questo perché Alexander Terekhov ha proposto un algoritmo circa un decennio fa che è completamente giusto. Consente al sistema operativo di decidere se il thread successivo per ottenere il mutex è un lettore o uno scrittore e il sistema operativo è completamente all'oscuro sul fatto che il thread successivo sia un lettore o uno scrittore.

A causa di questo algoritmo, la necessità di specificare se un lettore o uno scrittore è preferito scompare. Per quanto ne so, le librerie boost sono ora (boost 1.52) implementate con questo algoritmo equo.

L'algoritmo di Terekhov consiste nell'avere il mutex di lettura/scrittura costituito da due porte: gate1 e gate2. Solo un filo alla volta può passare attraverso ogni porta. Le porte possono essere implementate con un mutex e due variabili di condizione.

Sia i lettori che gli scrittori cercano di passare attraverso gate1. Per poter passare attraverso gate1 deve essere vero che il thread di uno scrittore non è attualmente all'interno di gate1. Se c'è, il thread tenta di passare attraverso i blocchi gate1.

Una volta che un thread del lettore passa attraverso gate1, ha letto la proprietà del mutex.

Quando un thread di writer passa attraverso gate1, deve anche passare attraverso gate2 prima di ottenere la proprietà di scrittura del mutex. Non può passare attraverso gate2 finché il numero di lettori all'interno di gate1 non scende a zero.

Questo è un algoritmo giusto, perché quando ci sono solo 0 o più lettori all'interno di Gate1, spetta al sistema operativo se il thread successivo per ottenere all'interno di Gate1 è un lettore o scrittore. Uno scrittore diventa "prioritario" solo dopo che è passato attraverso gate1, ed è quindi il prossimo in fila per ottenere la proprietà del mutex.

Ho usato il tuo esempio compilato su un'implementazione di esempio di ciò che alla fine è diventato shared_timed_mutex in C++ 14 (con piccole modifiche al tuo esempio). Il codice qui sotto lo chiama shared_mutex che è il nome che aveva quando è stato proposto.

ho ottenuto le seguenti uscite (tutti con lo stesso eseguibile):

volte:

Worked finished her work 
Worked finished her work 
Grabbed exclusive lock, killing system 
KILLING ALL WORK 
Workers are on strike. This worker refuses to work 
Workers are on strike. This worker refuses to work 

e talvolta:

Worked finished her work 
Grabbed exclusive lock, killing system 
KILLING ALL WORK 
Workers are on strike. This worker refuses to work 
Workers are on strike. This worker refuses to work 
Workers are on strike. This worker refuses to work 

E a volte:

Worked finished her work 
Worked finished her work 
Worked finished her work 
Worked finished her work 
Grabbed exclusive lock, killing system 
KILLING ALL WORK 

io credo che dovrebbe essere teoricamente possibile ottenere anche altri risultati, anche se non l'ho confermato sperimentalmente.

Nell'interesse di una completa informativa, ecco il codice esatto ho eseguito:

#include "../mutexes/shared_mutex" 
#include <thread> 
#include <chrono> 
#include <iostream> 

using namespace std; 
using namespace ting; 

mutex outLock; 
shared_mutex workerAccess; 
bool shouldIWork = true; 

class WorkerKiller 
{ 
public: 

    void operator()() 
    { 
     unique_lock<shared_mutex> lock(workerAccess); 

     cout << "Grabbed exclusive lock, killing system" << endl; 
     this_thread::sleep_for(chrono::seconds(2)); 
     shouldIWork = false; 
     cout << "KILLING ALL WORK" << endl; 
    } 

private: 
}; 

class Worker 
{ 
public: 

    Worker() 
    { 
    } 

    void operator()() 
    { 
     shared_lock<shared_mutex> lock(workerAccess); 

     if (!shouldIWork) { 
      lock_guard<mutex> _(outLock); 
      cout << "Workers are on strike. This worker refuses to work" << endl; 
     } else { 
      this_thread::sleep_for(chrono::seconds(1)); 

      lock_guard<mutex> _(outLock); 
      cout << "Worked finished her work" << endl; 
     } 
    } 
}; 

int main() 
{ 
    Worker w1; 
    Worker w2; 
    Worker w3; 
    Worker w4; 
    WorkerKiller wk; 

    thread workerThread1(w1); 
    thread workerThread2(w2); 

    thread workerKillerThread(wk); 

    thread workerThread3(w3); 
    thread workerThread4(w4); 

    workerThread1.join(); 
    workerThread2.join(); 
    workerKillerThread.join(); 
    workerThread3.join(); 
    workerThread4.join(); 

    return 0; 
} 
+0

Sì, sono appena tornato a questa domanda per pubblicare la stessa risposta. Sei davvero corretto. Boost ora utilizza un'implementazione equa e non è possibile controllare quale viene selezionato. Grazie mille per la tua spiegazione dettagliata. – anoneironaut

+0

Il documento Blooming sembra mancante nel nuovo sito all'indirizzo http://howardhinnant.github.io/. Non so come risolvere quello :) – sehe

1

una ricerca su Google di "fame blocco aumentare condivisa" alzato questo link:

Sembra "upgrade" potrebbe essere la chiave. Vedi anche:

+0

Hi Paul. Ho dato un'occhiata a quegli articoli. Sono state ottime letture, ma sono ancora un po 'perso per decidere se posso impostare questi mutex come preferisco Write. L'ultimo articolo sembra delineare un modello mutex che anche Boost è simile ma presenta differenze chiave. In quel modello è sicuramente scritto preferito. Ma in boost sembra essere OS specifico? Ho trovato queste informazioni anche nella documentazione 1.35: http://www.boost.org/doc/libs/1_34_0/doc/html/thread/concepts.html#thread.concepts.read-write-scheduling-policies.inter -classe. Inoltre ho provato ad aumentare la priorità del mio scrittore. – anoneironaut

+0

@HowardHinnant Ho provato a [correggere questo collegamento] (http://stackoverflow.com/posts/12082441/revisions). Magari potresti rivederlo – sehe