2011-01-07 9 views
6

Desde mi entendimiento de la palabra clave mutable, uno de sus usos principales es el almacenamiento en caché de datos y su computación cuando sea necesario. Como pueden cambiar (aunque sean const) no sería inseguro o inútil usarlos? La parte de almacenamiento en caché modifica los datos, por lo que debería haber un bloqueo y, según entiendo, al escribir en multihebra, los datos NUNCA deben cambiar y las copias deben realizarse y enviarse/encadenarse juntas.¿La palabra clave C++ mutable es incorrecta para los hilos?

¿No tiene sentido o es malo usar la palabra clave mutable de C++?

+2

Diría que en realidad malinterpretas la palabra clave 'mutable'. ¿De dónde sacaste esta idea de que está destinado a afectar el almacenamiento en caché de datos? –

+3

@Jonathan: el almacenamiento en caché de datos es un uso muy común de los miembros de datos 'mutables'. Si tiene una función de miembro const que realiza alguna tarea costosa y sabe que la función se llama con frecuencia, una optimización típica es introducir un miembro de datos mutable para almacenar en caché los resultados de la costosa tarea para que las llamadas futuras a esa función miembro sean más rápidas . –

+5

Sí, pero ese es un caso de uso, no el significado de la palabra clave. –

Respuesta

15

¿No tiene sentido o es malo usar la palabra clave mutable de C++?

No; la palabra clave mutable es A Good Thing. mutable se puede utilizar para separar el estado observable de un objeto de los contenidos internos del objeto.

Con el ejemplo de "datos en caché" que describe (un uso muy común de mutable), permite que la clase realice optimizaciones "debajo de las coberturas" que en realidad no modifican el estado observable.

Con respecto al acceso a un objeto de múltiples hilos, sí, debe tener cuidado. En general, si una clase está diseñada para acceder desde múltiples hilos y tiene variables mutable, debe sincronizar la modificación de esas variables internamente. Tenga en cuenta, sin embargo, que el problema es realmente más conceptual. Es fácil pensar que:

  1. Todos mis procesos únicamente llaman métodos constantes en este objeto compartido
  2. métodos constantes no modifican el objeto sobre el cual son llamados
  3. Si un objeto no se modifica , no necesito para sincronizar el acceso a la misma
  4. Por lo tanto, no necesito para sincronizar el acceso a este objeto

este argumento es erróneo debido a que (2) es falsa: métodos constantes de hecho puede modif y miembros de datos mutables. El problema es que es muy, muy fácil pensar que este argumento es correcto.

La solución a este problema no es fácil: efectivamente, solo tiene que ser extremadamente cuidadoso al escribir código multiproceso y estar absolutamente seguro de que comprende cómo se implementan los objetos compartidos entre subprocesos o qué concurrencia garantiza.

+1

Si una clase está diseñada para acceder desde múltiples hilos y tiene * cualquier * variable no constante, necesita sincronizar la modificación de esas variables. –

+2

@Charles: Bueno, creo que se puede argumentar que es más un problema con los miembros de datos 'mutables'. Si tiene un número de hilos con referencias frecuentes a un objeto compartido, a menudo es razonable suponer que puede llamar a esas funciones miembros sin ninguna sincronización. Es fácil olvidarse de lo que es mutable e incorrectamente pensar que las funciones miembro de la función no pueden mutar el estado subyacente. –

+0

+1 para la respuesta clara, correcta y lógica! – Nawaz

2

No, la palabra clave mutable es para que pueda tener campos dentro de un objeto que pueden cambiar incluso cuando el objeto es const, como para metadatos que no son parte de las propiedades de un objeto sino su gestión (como contadores, etc.). No tiene nada que ver con enhebrar.

+6

Enhebrar _es_ un problema aquí. Si tiene un número de subprocesos, todos con referencias de referencias a algún objeto compartido, es fácil suponer que todos estos subprocesos pueden llamar a funciones miembro de const en ese objeto sin ningún tipo de sincronización. Sin embargo, si hay miembros de datos mutables que son modificados por esas funciones miembro de const, debe asegurarse de que esas modificaciones estén sincronizadas correctamente. –

+1

Lo que quise decir es que la palabra clave en sí no tiene nada que ver con el enhebrado, como lo hace uno como 'volátil '. Y * nunca * asumo que los métodos de invocación de un objeto const a partir de dos hilos funcionan, no por esto, sino por el hecho de que mientras 'const' solo significa que el objeto no cambia, no dice nada acerca de los objetivos de cualquiera o las referencias que podría tener, por lo que podría modificar algo incorrectamente incluso a través de las referencias de const sin datos mutables. – Mehrdad

+0

Se equivoca acerca de "mutable", permite que los miembros vars marcados como "mutable" se modifiquen en métodos marcados const, independientemente de si la instancia a la que se llama es const o no. –

7

En el extremo opuesto, la mayor parte de mi código multiproceso requiere el uso de la mutable palabra clave:

class object { 
    type m_data; 
    mutable mutex m_mutex; 
public: 
    void set(type const & value) { 
     scoped_lock lock(m_mutex); 
     m_data = value; 
    } 
    type get() const { 
     scoped_lock lock(m_mutex); 
     return m_data; 
    } 
}; 

El hecho de que el método get no modifica el estado de la object es declarado por medio de la palabra clave const.Pero sin el modificador mutable aplicado a la declaración del atributo mutex, el código no podría bloquear o liberar el mutex --ambas operaciones claramente modificar el mutex, incluso si no modifican el object.

Incluso puede hacer que el atributo data se modifique si puede evaluarse de forma diferida y el costo es alto, siempre que bloquee el objeto. Este es el uso del caché al que se refiere en la pregunta.

El modificador mutable no es un problema con el código multiproceso, solo cuando intenta hacer sin enclavamiento multihilo. Y al igual que con todos programación sin cerradura, debe ser muy cuidado con lo que hace, independientemente de const o const. Puede escribir código multiproceso perfectamente inseguro que llame a los métodos const en objetos sin atributos mutable. El ejemplo simple sería eliminar el mutex del código anterior y tener N hilos para realizar solo get() s, mientras que otro hilo realiza set() s. El hecho de que get() es const no es garantía de que no obtendrá resultados no válidos si otro hilo está modificando.

+0

¿No sería igual de seguro no bloquear get()? La única forma en que puedo ver que m_data es inválido es si m_data pasa a ser> 32 o 64 bits y tiene que hacer varias asignaciones para copiar el objeto completo. ¿Es esta la única razón por la cual? si m_data era un puntero, entonces no veo ningún motivo para bloquearlo. –

+0

@ acidzombie24: Todo depende de la arquitectura con la que trabaje y de otros factores, el idioma no impone la atomicidad de las lecturas/escrituras. Si 'type' es una estructura que contiene 4 caracteres, la lectura probablemente no sea atómica. Incluso si se trata de un único valor de 32 bits en un procesador Intel moderno, las lecturas podrían ser no atómicas si los datos (en este caso, 'objeto') no están alineados con la palabra. Nuevamente, al escribir código sin bloqueo, debe tener * muchas * cosas en cuenta. –

+0

Para su ejemplo específico, recomendaré un bloqueo ReadWrite [enlace] (https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock) – Niki

Cuestiones relacionadas