Serrures de lecteur / graveur en C ++

Je recherche un bon verrou de lecture / écriture en C ++. Nous avons un cas d’utilisation d’un seul auteur peu fréquent et de nombreux lecteurs fréquents et nous aimerions optimiser cela. Préféré je voudrais une solution multi-plateforme, cependant un seul Windows serait acceptable.

Les nouvelles versions de boost :: thread ont des verrous en lecture / écriture (1.35.0 et versions ultérieures, apparemment les versions précédentes ne fonctionnaient pas correctement).

Ils ont les noms shared_lock , unique_lock et upgrade_lock et fonctionnent sur un shared_mutex .

L’utilisation de logiciels pré-testés standard est toujours une bonne chose (par exemple, Boost comme autre solution suggérée), mais c’est quelque chose qui n’est pas trop difficile à construire vous-même. Voici une petite mise en œuvre stupide tirée d’un projet de ma mine:

 #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 n’étant pas vraiment Windows, mais l’idée générale est là. Cette implémentation est légèrement biaisée envers les écrivains (une horde d’écrivains peut affamer les lecteurs indéfiniment); modifiez simplement writer_unlock si vous préférez que la balance soit writer_unlock .

Oui, c est C et pas C ++. La traduction est un exercice laissé au lecteur.

modifier

Greg Rogers a souligné que la norme POSIX spécifie pthread_rwlock_* . Cela n’aide pas si vous ne possédez pas de pthreads , mais cela m’a incité à me souvenir: Pthreads-w32 devrait fonctionner! Au lieu de porter ce code sur des non- pthreads pour votre propre usage, utilisez simplement Pthreads-w32 sous Windows et les pthreads natifs partout ailleurs.

Vous pouvez utiliser boost pour créer un verrou en lecture-écriture:

 #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 } 

Quoi que vous décidiez d’utiliser, comparez votre charge de travail à des verrous simples, car les verrous en lecture / écriture ont tendance à être plus lents que les simples mutex, alors qu’il n’y a pas de conflit.

Voici une référence

Edit: Le lien MSDN Magazine n’est plus disponible. L’article CodeProject est maintenant disponible sur https://www.codeproject.com/Articles/32685/Testing-reader-writer-locks et le résume bien. J’ai également trouvé un nouveau lien MSDN sur les objects de synchronisation composés .

Il existe un article sur les verrous de lecteur-écrivain sur MSDN qui présente certaines de leurs implémentations. Il introduit également le verrou de lecture / écriture Slim, une primitive de synchronisation du kernel introduite avec Vista. Il existe également un article CodeProject sur la comparaison de différentes implémentations (y compris celles de l’article MSDN).

Les blocs de construction Intel Thread fournissent également quelques variantes de rw_lock:

http://www.threadingbuildingblocks.org/

Ils ont un spin_rw_mutex pour des périodes de conflit très courtes et un queueing_rw_mutex pour des périodes de conflit plus longues. Le premier peut être utilisé dans un code particulièrement sensible aux performances. Ce dernier est plus comparable en performances que celui fourni par Boost.Thread ou en utilisant directement pthreads. Mais profilez-vous pour vous assurer que vous êtes gagnant pour vos habitudes d’access.

Depuis la version 1.35.0, Boost.Thread prend déjà en charge les verrous de lecture / écriture. La bonne chose à ce sujet est que l’implémentation est grandement multi-plateforme, évaluée par les pairs et est en fait une implémentation de référence pour la future norme C ++ 0x .

Je peux recommander la bibliothèque ACE , qui fournit une multitude de mécanismes de locking et est scope sur diverses plates-formes.

Selon les conditions limites de votre problème, vous pouvez trouver les classes suivantes utiles:

  • ACE_RW_Process_Mutex
  • ACE_Write_Guard et ACE_Read_Guard
  • ACE_Condition

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

Voici une implémentation correcte et légère adaptée à la plupart des tâches.

Classe de locking de synchronisation Multiple-Reader, Single-Writer pour Win32 par Glenn Slayde

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

C ++ 17 prend en charge std::shared_mutex . Il est pris en charge dans MSVC ++ 2015 et 2017.

Vous pouvez copier l’excellent ReentrantReadWriteLock de Sun. Il comprend des fonctionnalités telles que l’équité facultative, la rétrogradation des verrous et, bien sûr, la réentrée.

Oui, c’est en Java, mais vous pouvez facilement le lire et le transposer en C ++, même si vous ne connaissez aucun Java. La documentation à laquelle je suis lié contient toutes les propriétés comportementales de cette implémentation afin que vous puissiez vous assurer de faire ce que vous voulez.

Si rien d’autre, c’est un guide.