Lettori Reader / Writer in C ++

Sto cercando un buon blocco lettore / scrittore in C ++. Abbiamo un caso d’uso di un singolo scrittore poco frequente e molti lettori frequenti e vorremmo ottimizzare per questo. Preferirei una soluzione multipiattaforma, tuttavia una sola Windows sarebbe accettabile.

Le versioni più recenti di boost :: thread hanno blocchi di lettura / scrittura (1.35.0 e versioni successive, apparentemente le versioni precedenti non funzionavano correttamente).

Hanno i nomi shared_lock , unique_lock e unique_lock e operano su shared_mutex .

L’uso di materiale pre-testato standard pre-costruito è sempre buono (ad esempio, Boost come suggerito da un’altra risposta), ma questo è qualcosa che non è troppo difficile da build da solo. Ecco una piccola e stupida implementazione tratta da un mio progetto:

 #include  struct rwlock { pthread_mutex_t lock; pthread_cond_t read, write; unsigned readers, writers, read_waiters, write_waiters; }; void reader_lock(struct rwlock *self) { pthread_mutex_lock(&self->lock); if (self->writers || self->write_waiters) { self->read_waiters++; do pthread_cond_wait(&self->read, &self->lock); while (self->writers || self->write_waiters); self->read_waiters--; } self->readers++; pthread_mutex_unlock(&self->lock); } void reader_unlock(struct rwlock *self) { pthread_mutex_lock(&self->lock); self->readers--; if (self->write_waiters) pthread_cond_signal(&self->write); pthread_mutex_unlock(&self->lock); } void writer_lock(struct rwlock *self) { pthread_mutex_lock(&self->lock); if (self->readers || self->writers) { self->write_waiters++; do pthread_cond_wait(&self->write, &self->lock); while (self->readers || self->writers); self->write_waiters--; } self->writers = 1; pthread_mutex_unlock(&self->lock); } void writer_unlock(struct rwlock *self) { pthread_mutex_lock(&self->lock); self->writers = 0; if (self->write_waiters) pthread_cond_signal(&self->write); else if (self->read_waiters) pthread_cond_broadcast(&self->read); pthread_mutex_unlock(&self->lock); } void rwlock_init(struct rwlock *self) { self->readers = self->writers = self->read_waiters = self->write_waiters = 0; pthread_mutex_init(&self->lock, NULL); pthread_cond_init(&self->read, NULL); pthread_cond_init(&self->write, NULL); } 

pthreads non è realmente nativo di Windows, ma l’idea generale è qui. Questa implementazione è leggermente distorta nei confronti degli scrittori (un’orda di scrittori può far morire di fame i lettori a tempo indefinito); basta modificare writer_unlock se preferisci che l’equilibrio sia il contrario.

Sì, questo è C e non C ++. La traduzione è un esercizio lasciato al lettore.

modificare

Greg Rogers ha sottolineato che lo standard POSIX specifica pthread_rwlock_* . Questo non aiuta se non si dispone di pthreads , ma ha stimolato la mia mente a ricordare: Pthreads-w32 dovrebbe funzionare! Invece di portare questo codice a non- pthreads per uso personale, basta usare Pthreads-w32 su Windows e pthreads nativi ovunque.

Puoi usare boost per creare un blocco di lettura / scrittura:

 #include  #include  typedef boost::shared_mutex Lock; typedef boost::unique_lock< Lock > WriteLock; typedef boost::shared_lock< Lock > ReadLock; Lock myLock; void ReadFunction() { ReadLock r_lock(myLock); //Do reader stuff } void WriteFunction() { WriteLock w_lock(myLock); //Do writer stuff } 

Qualunque cosa tu decida di utilizzare, confronta il tuo carico di lavoro con semplici blocchi, in quanto i blocchi di lettura / scrittura tendono a essere 3-40 volte più lenti del semplice mutex, quando non c’è contesa.

Ecco alcuni riferimenti

Modifica: il link di MSDN Magazine non è più disponibile. L’articolo CodeProject è ora disponibile su https://www.codeproject.com/Articles/32685/Testing-reader-writer-locks e lo riassume molto bene. Inoltre ho trovato un nuovo collegamento MSDN su Compound Synchronization Objects .

C’è un articolo sui blocchi lettore-scrittore su MSDN che presenta alcune implementazioni di essi. Introduce anche il blocco Slim reader / writer, una primitiva di sincronizzazione del kernel introdotta con Vista. C’è anche un articolo di CodeProject sul confronto tra diverse implementazioni (incluse quelle degli articoli di MSDN).

Intel Thread Building Blocks fornisce anche un paio di varianti rw_lock:

http://www.threadingbuildingblocks.org/

Hanno uno spin_rw_mutex per brevissimi periodi di contesa e un queueing_rw_mutex per lunghi periodi di contesa. Il primo può essere utilizzato in un codice particolarmente sensibile alle prestazioni. Quest’ultimo è più comparabile in termini di prestazioni a quello fornito da Boost.Thread o direttamente utilizzando pthreads. Ma profilo per assicurarsi quale sia una vittoria per i tuoi modelli di accesso.

Boost.Thread ha già rilasciato la versione 1.35.0 e supporta già i blocchi di lettore-scrittore. La cosa positiva è che l’implementazione è notevolmente multi-piattaforma, peer-reviewed ed è in realtà un’implementazione di riferimento per il prossimo standard C ++ 0x .

Posso consigliare la libreria ACE , che fornisce una moltitudine di meccanismi di blocco e viene portata su varie piattaforms.

A seconda delle condizioni limite del tuo problema, potresti trovare utili le seguenti classi:

  • ACE_RW_Process_Mutex
  • ACE_Write_Guard e ACE_Read_Guard
  • ACE_Condition

http://www.codeproject.com/KB/threads/ReaderWriterLock.aspx

Ecco una buona e leggera implementazione adatta per la maggior parte delle attività.

Classe di blocco sincronizzazione sincronizzatore a singolo lettore per Win32 di Glenn Slayde

http://www.glennslayden.com/code/win32/reader-writer-lock

C ++ 17 supporta std::shared_mutex . È supportato in MSVC ++ 2015 e 2017.

Potresti copiare l’eccellente ReentrantReadWriteLock di Sun. Comprende funzionalità quali l’equità opzionale, il downgrade del blocco e, naturalmente, la rientranza.

Sì, è in Java, ma puoi leggerlo e trasporlo facilmente in C ++, anche se non conosci Java. La documentazione che ho collegato contiene tutte le proprietà comportamentali di questa implementazione in modo che tu possa fare in modo che faccia ciò che vuoi.

Se non altro, è una guida.