c++ - semaforos - semaforo lector
Bloqueos de lector/escritor en C++ (12)
C ++ 17 es compatible con std::shared_mutex
. Es compatible con MSVC ++ 2015 y 2017.
Estoy buscando un buen bloqueo de lector / escritor en C ++. Tenemos un caso de uso de un escritor poco frecuente y muchos lectores frecuentes y nos gustaría optimizarlo. Preferible, me gustaría una solución multiplataforma; sin embargo, solo una de Windows sería aceptable.
Clase de bloqueo de sincronización de varios escritores y un solo escritor para Win32 por Glenn Slayde
Editar: El enlace de MSDN Magazine ya no está disponible. El artículo de CodeProject ahora está disponible en https://www.codeproject.com/Articles/32685/Testing-reader-writer-locks y lo resume bastante bien. También encontré un nuevo enlace de MSDN sobre Objetos de sincronización compuesta .
Hay un article sobre bloqueos lector-escritor en MSDN que presenta algunas implementaciones de ellos. También presenta el bloqueo Slim reader / writer, una primitiva de sincronización de kernel presentada con Vista. También hay un artículo de CodeProject sobre la comparación de diferentes implementaciones (incluidas las del artículo de MSDN).
El uso de elementos previamente probados y precompilados siempre es bueno (por ejemplo, Boost, ya que se sugiere otra respuesta), pero esto es algo que no es demasiado difícil de construir. Aquí hay una pequeña y estúpida implementación extraída de un proyecto mío:
#include <pthread.h>
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
no es realmente nativo de Windows, pero la idea general está aquí. Esta implementación es un poco sesgada hacia los escritores (una horda de escritores puede matar de hambre a los lectores de forma indefinida); simplemente modifique writer_unlock
si prefiere que el saldo sea al revés.
Sí, esto es C y no C ++. La traducción es un ejercicio dejado al lector.
Editar
Greg Rogers señaló que el estándar POSIX especifica pthread_rwlock_*
. Esto no ayuda si no tienes pthreads
, pero me revolvió la mente al recordar: ¡ Pthreads-w32 debería funcionar! En lugar de portar este código a no pthreads
para su propio uso, simplemente use Pthreads-w32 en Windows y pthreads
nativos en cualquier otro lado.
Independientemente de lo que decida usar, compare su carga de trabajo con cerraduras simples, ya que los bloqueos de lectura / escritura tienden a ser 3-40 veces más lentos que el mutex simple, cuando no hay contención.
Aquí hay alguna referencia
Las versiones más recientes de boost::thread tienen bloqueos de lectura / escritura (1.35.0 y posterior, aparentemente las versiones anteriores no funcionaron correctamente).
Tienen los nombres shared_lock
, unique_lock
, y upgrade_lock
y operan en shared_mutex
.
Los bloques de construcción de subprocesos Intel también proporcionan un par de variantes de rw_lock:
http://www.threadingbuildingblocks.org/
Tienen un spin_rw_mutex durante periodos de contención muy cortos y un queueing_rw_mutex para periodos de contención más largos. El primero se puede usar en un código especialmente sensible al rendimiento. Esta última es más comparable en rendimiento a la proporcionada por Boost. Conversa o usa directamente pthreads. Pero perfil para asegurarse de cuál es una ganancia para sus patrones de acceso.
Podría copiar el excelente ReentrantReadWriteLock Incluye características tales como equidad opcional, reducción de bloqueo y, por supuesto, reentrada.
Sí, está en Java, pero puede leerlo y transponerlo fácilmente a C ++, incluso si no conoce Java. La documentación a la que he vinculado contiene todas las propiedades de comportamiento de esta implementación para que pueda asegurarse de que hace lo que desea.
Si nada más, es una guía.
Puede usar boost para crear un bloqueo de lectura y escritura:
#include <boost/thread/locks.hpp>
#include <boost/thread/shared_mutex.hpp>
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
}
Puedo recomendar la biblioteca ACE , que proporciona una multitud de mecanismos de bloqueo y se transporta a varias plataformas.
Dependiendo de las condiciones límite de su problema, puede encontrar útiles las siguientes clases:
-
ACE_RW_Process_Mutex
-
ACE_Write_Guard
yACE_Read_Guard
-
ACE_Condition
Boost.Thread desde la versión 1.35.0 ya admite bloqueos lector-escritor. Lo bueno de esto es que la implementación es en gran medida multiplataforma, revisada por pares, y en realidad es una implementación de referencia para el próximo estándar C ++ 0x .
http://www.codeproject.com/KB/threads/ReaderWriterLock.aspx
Aquí hay una implementación buena y ligera adecuada para la mayoría de las tareas.