31 votos

Debe mutexes ser mutable?

No estoy seguro si esto es un estilo de pregunta, o algo que tiene un duro regla...

Si quiero mantener el método público de la interfaz como const posible, pero hace que el objeto thread safe, debo usar mutable mutexes? En general, este es un buen estilo, o en caso de un no-const método de la interfaz de ser preferido? Por favor justifique su opinión.

34voto

paercebal Puntos 38526

El oculto pregunta es: ¿dónde se coloca el mutex la protección de su clase?

Como resumen, digamos que usted desea leer el contenido de un objeto que está protegida por un mutex.

La "lectura" método debe ser semánticamente "const", ya que no cambia el objeto en sí. Pero para leer el valor, usted necesita para bloquear un mutex, extraer el valor y, a continuación, desbloquear la exclusión mutua, es decir, la exclusión mutua en sí debe ser modificado, es decir, la exclusión mutua en sí misma no puede ser "const".

Si el mutex es externo

Entonces todo está bien. El objeto puede ser "const", y el mutex no necesita ser:

Mutex mutex ;

int foo(const Object & object)
{
   Lock<Mutex> lock(mutex) ;
   return object.read() ;
}

En mi humilde opinión, esta es una mala solución, porque nadie podría reutilizar el mutex para proteger algo más. Incluyendo a usted. De hecho, se traicionará a sí mismo porque, si el código es bastante complejo, usted acaba de ser confundido acerca de lo que este o aquel objeto mutex es exactamente protección.

Yo sé: yo era víctima de ese problema.

Si el mutex es interna

Para la encapsulación, debes poner el mutex tan cerca como sea posible del objeto es la protección.

Por lo general, usted va a escribir una clase con un mutex en el interior. Pero antes o después, usted necesita para proteger algunos complejo STL estructura, o cualquier cosa escrita por otro sin exclusión mutua en el interior (que es una buena cosa).

Una buena manera de hacer esto es para obtener el objeto original con un heredar de la plantilla de la adición de la exclusión mutua de la característica:

template <typename T>
class Mutexed : public T
{
   public :
      Mutexed() : T() {}
      // etc.

      void lock()   { this->m_mutex.lock() ; }
      void unlock() { this->m_mutex.unlock() ; } ;

   private :
      Mutex m_mutex ;
}

De esta manera, se puede escribir:

int foo(const Mutexed<Object> & object)
{
   Lock<Mutexed<Object> > lock(object) ;
   return object.read() ;
}

El problema es que no va a funcionar porque object es constante, y el objeto de bloqueo está llamando a la no-const lock y unlock métodos.

El Dilema

Si usted cree const está limitada a bit a bit const objetos, entonces estás jodido, y debe volver a la "externa mutex solución".

La solución es admitir const es más semántico calificador (como volatile cuando se utiliza como un método de calificación de las clases). Usted está ocultando el hecho de que la clase no está plenamente const pero asegúrese de proporcionar una implementación que mantiene la promesa de que la significativa partes de la clase no será cambiado cuando se llama a un const método.

A continuación, debe declarar su mutex mutable, y el de bloqueo/desbloqueo de los métodos de const:

template <typename T>
class Mutexed : public T
{
   public :
      Mutexed() : T() {}
      // etc.

      void lock()   const { this->m_mutex.lock() ; }
      void unlock() const { this->m_mutex.unlock() ; } ;

   private :
      mutable Mutex m_mutex ;
}

La interna de la exclusión mutua es una solución buena en mi humilde opinión: Tener a los objetos declarados uno cerca del otro en una mano, y con ellos agregados en un contenedor en el otro lado, es la misma cosa en la final.

Pero la agregación tiene las siguientes ventajas:

  1. Es más natural (bloquear el objeto antes de acceder a ella)
  2. Un objeto, un objeto mutex. Como el estilo de código obliga a seguir este patrón, disminuye interbloqueo de riesgos debido a una exclusión mutua de proteger un objeto único (y no varios objetos que realmente no recuerdo), y un objeto, serán protegidos por un mutex sólo (y no por varias de exclusión mutua que debe ser bloqueado en el orden correcto)
  3. El mutexed clase anterior puede ser utilizado para cualquier clase de

Por lo tanto, mantener su mutex tan cerca como sea posible a la mutexed objeto (por ejemplo, utilizando la Mutexed construir arriba), y para ir a por la mutable clasificatorio para la exclusión mutua.

Editar 2013-01-04

Al parecer, Herb Sutter tienen el mismo punto de vista: en Su presentación acerca de los "nuevos" significados de const y mutable en C++11 es muy esclarecedor:

http://herbsutter.com/2013/01/01/video-you-dont-know-const-and-mutable/

21voto

Alexandre C. Puntos 31758

[Respuesta editado]

Básicamente el uso de const métodos con los mutexes es una buena idea (no devuelven referencias por el camino, asegúrese de devolver por valor), al menos para indicar que no se modifique el objeto. Los Mutexes no debe ser constante, sería una descarada mentira para definir bloqueo/desbloqueo de métodos como const...

En realidad esto (y memoization) son los únicos usos justos veo de la mutable de palabras clave.

También se puede utilizar un mutex que es externo a su objeto: organizar todos sus métodos a ser reentrante, y que el usuario gestionar el bloqueo de la misma : { lock locker(the_mutex); obj.foo(); } no es tan difícil de escribir, y

{
    lock locker(the_mutex);
    obj.foo();
    obj.bar(42);
    ...
}

tiene la ventaja de que no requieren de dos mutex cerraduras (y usted está garantizado el estado del objeto no cambia).

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X