Oigo que const significa thread-safe en C++11. Es eso cierto?

Significa eso const es ahora el equivalente de Java‘s synchronized?

Están funcionando fuera de palabras clave?

  • El C++-faq es generalmente administrado por el C++ de la comunidad, y usted podría tenido la amabilidad de venir y nos piden opiniones en nuestro chat.
  • Yo no era consciente del C++-preguntas más frecuentes y sus buenas costumbres, se sugirió en un comentario.
  • Donde escuchaste eso const significa thread-safe?
  • B: Herb Sutter y Bjarne Stroustrup decían así en el Estándar de C++ de la Fundación, vea el enlace en la parte inferior de la respuesta.
  • NOTA PARA LOS que VIENEN AQUÍ: la verdadera pregunta NO es si const significa thread-safe. Que sería absurdo, ya que de lo contrario esto podría significar que usted debería ser capaz de seguir adelante y marcar cada hilo método seguro como const. Más bien, la pregunta que nos estamos preguntando es const IMPLICA thread-safe, y que es lo que esta discusión es acerca de.
InformationsquelleAutor K-ballo | 2013-01-02

1 Comentario

  1. 129

    Oigo que const significa thread-safe en C++11. Es eso cierto?

    Es algo cierto…

    Esto es lo que el Lenguaje Estándar tiene que decir en el hilo de seguridad:

    [1.10/4]
    Dos evaluaciones de expresiones conflicto si uno de ellos modifica una ubicación de memoria (1.7) y el otro uno de los accesos o modifica la misma ubicación de memoria.

    [1.10/21]
    La ejecución de un programa contenga un de datos de la carrera si contiene dos dibujos acciones en diferentes hilos, al menos, uno de los cuales no es atómica, y tampoco ocurre antes que el otro. Dichos datos los resultados de la carrera en un comportamiento indefinido.

    que no es otra cosa que la condición suficiente para que un de datos de la carrera a ocurrir:

    1. Hay dos o más acciones que se están llevando a cabo al mismo tiempo en una determinada cosa; y
    2. Al menos una de ellas es la de escribir.

    La de la Biblioteca Estándar de se basa en que, yendo un poco más allá:

    [17.6.5.9/1]
    Esta sección especifica los requisitos que las implementaciones se reunirá para evitar carreras de datos (1.10). Cada estándar de la función de la biblioteca deberá satisfacer cada requerimiento, a menos que se especifique lo contrario. Implementaciones pueden prevenir las carreras de datos en casos distintos de los especificados a continuación.

    [17.6.5.9/3]
    Un estándar de C++ función de la biblioteca no podrá, directa o indirectamente modificar objetos (1.10) accesible por otros hilos que el subproceso actual, a menos que los objetos se accede directa o indirectamente a través de la función noconst argumentos, incluyendo this.

    que en simples palabras, dice que se espera que las operaciones en const objetos a ser thread-safe. Esto significa que el de la Biblioteca Estándar de no introducir datos de la carrera, mientras las operaciones en const objetos de su propio tipo, ya sea

    1. Se componen enteramente de lecturas, es decir, no hay ningún escribe–; o
    2. Internamente sincroniza escribe.

    Si esta expectativa no se cumple para uno de sus tipos, usando directamente o indirectamente junto con cualquiera de los componentes de la de la Biblioteca Estándar de puede resultar en un de datos de la carrera. En conclusión, const significa thread-safe de la de la Biblioteca Estándar de punto de vista. Es importante tener en cuenta que esto es simplemente un contrato y no ser impuesta por el compilador, si se rompe se obtiene un comportamiento indefinido y usted está en sus el propio. Si const está presente o no, no afectará a la generación de código, al menos no en el respeto a carreras de datos–.

    Significa eso const es ahora el equivalente de Java‘s synchronized?

    No. No, en absoluto…

    Considerar los siguientes excesivamente simplificada de la clase que representa un rectángulo:

    class rect {
        int width = 0, height = 0;
    
    public:
        /*...*/
        void set_size( int new_width, int new_height ) {
            width = new_width;
            height = new_height;
        }
        int area() const {
            return width * height;
        }
    };

    La miembro de la función de area es thread-safe; no porque su const, pero debido a que se componen enteramente de las operaciones de lectura. No hay ningún escribe involucrados, y al menos uno escribe involucrados es necesario para que un de datos de la carrera a ocurrir. Eso significa que usted puede llamar a area de tantos hilos como usted quiere y usted va a obtener los resultados correctos todo el tiempo.

    Tenga en cuenta que esto no significa que rect es thread-safe. De hecho, es fácil ver cómo, si una llamada a area a suceder al mismo tiempo que una llamada a set_size en un determinado rect, entonces area podría terminar de computación de su resultado basado en un viejo ancho y una altura (o incluso en los confusos valores).

    Pero eso está bien, rect no const así que no es aún espera ser thread-safe después de todo. Un objeto declarado const rect, por otro lado, sería thread-safe ya no escribe son posibles (y si usted está considerando const_cast-ing algo declarada originalmente const luego de llegar indefinido en el comportamiento de y eso es todo).

    Entonces, ¿qué significa entonces?

    Supongamos que-por el bien del argumento, que la multiplicación de las operaciones son muy costosos y es mejor evitarlos cuando sea posible. Podríamos calcular el área sólo si se solicita, y, a continuación, la memoria caché en el caso de que se solicite de nuevo en el futuro:

    class rect {
        int width = 0, height = 0;
    
        mutable int cached_area = 0;
        mutable bool cached_area_valid = true;
    
    public:
        /*...*/
        void set_size( int new_width, int new_height ) {
            cached_area_valid = ( width == new_width && height == new_height );
            width = new_width;
            height = new_height;
        }
        int area() const {
            if( !cached_area_valid ) {
                cached_area = width;
                cached_area *= height;
                cached_area_valid = true;
            }
            return cached_area;
        }
    };

    [En el caso de este ejemplo parece demasiado artificial, podría sustituir mentalmente int por un muy grande asignada dinámicamente entero que es inherentemente no thread-safe y para que las multiplicaciones son extremadamente costosos.]

    La miembro de la función de area ya no thread-safe, que está haciendo escribe ahora y no es internamente sincronizado. Esto es un problema? La llamada a area puede ocurrir como parte de un constructor de copia de otro objeto, constructor podría haber sido llamado por algunos operación en un contenedor estándar, y en ese momento la de la biblioteca estándar de espera que esta operación se comporte como un leer en lo que respecta a carreras de datos. Pero estamos haciendo escribe!

    Tan pronto como nos pusieron una rect en un contenedor estándar –directa o indirectamente– estamos entrando en un contrato con el de la Biblioteca Estándar de. A seguir haciendo escribe en un const función, mientras que aún cumpliendo con dicho contrato, necesitamos internamente sincronizar aquellos escribe:

    class rect {
        int width = 0, height = 0;
    
        mutable std::mutex cache_mutex;
        mutable int cached_area = 0;
        mutable bool cached_area_valid = true;
    
    public:
        /*...*/
        void set_size( int new_width, int new_height ) {
            if( new_width != width || new_height != height )
            {
                std::lock_guard< std::mutex > guard( cache_mutex );
    
                cached_area_valid = false;
            }
            width = new_width;
            height = new_height;
        }
        int area() const {
            std::lock_guard< std::mutex > guard( cache_mutex );
    
            if( !cached_area_valid ) {
                cached_area = width;
                cached_area *= height;
                cached_area_valid = true;
            }
            return cached_area;
        }
    };

    Nota que nos hizo la area función thread-safe, pero la rect todavía no thread-safe. Una llamada a area sucediendo al mismo tiempo que una llamada a set_size todavía puede terminar encima de calcular el valor incorrecto, ya que las asignaciones a la width y height no están protegidos por el mutex.

    Si realmente quería una thread-safe rect, debemos utilizar un primitivo de sincronización para proteger la no-thread-safe rect.

    Están funcionando fuera de palabras clave?

    Sí, lo son. Han estado funcionando de palabras clave desde el día uno.


    Fuente: Usted no sabe const y mutableHerb Sutter

    • Gracias por el formato que hablar, puede ser vale la pena marcado como c++-faq.
    • M.: Hecho. Yo no era consciente de que la etiqueta. Gracias.
    • Muy informativo. Esto parece prohibir referencia contada copy-on-write std::string implementaciones, el hilo de seguridad que entró en una reciente pregunta, porque std::string::string(const std:string& other) no está permitido mutar un recuento de uso en other. ¿Estás de acuerdo? ¿Qué consecuencias hay para std::shared_ptr?
    • (Que probablemente merece su propia pregunta o dos, que voy a publicar cuando tengo tiempo).
    • Voigt: es mi entendimiento de que el C++11 especificación para std::string está redactado de una manera que ya prohíbe de VACA. No recuerdo los detalles, aunque…
    • No. Se trataría sólo de evitar que estas cosas se sincronizado – es decir, no es seguro para subprocesos. C++11 ya explícitamente la prohibición de VACA en este pasaje en particular no tiene nada que ver con que, a pesar de, y no la prohibición de la VACA.
    • No veo cómo «no podrá, directa o indirectamente modificar» permite sincronizado modificación. Sí, ahí está el «accesible desde otros hilos» poco, pero la sincronización en una función no es hacer lo inaccesible de los demás.
    • eliminado mi comentario, porque me di cuenta de que llamar dos métodos (r.width() * r.height()) nunca sería hilo de seguridad, incluso si el objeto era seguro para subprocesos internamente.
    • se define con referencia a 1.10, que permite sincronizado correctamente escribe.
    • Que curioso, he visto esta charla por Herb Sutter esta tarde y yo tenía la esperanza de encontrar un lugar para hablar de ello. Gracias por cumplir mi deseo. Dicho esto, todavía hay algo que no me acababa de conseguir en la redacción de [17.6.5.9/3]: en lugar de escribir «no podrá, directa o indirectamente, modificar«, ¿no debería escribir «no podrá, directa o indirectamente introducir los datos de las razas»? Quiero decir, si un const función altera mutable variable entonces no modificar el valor almacenado en la ubicación de la memoria. Simplemente, esta modificación no se vuelve a crear carreras de datos por [1.10/21] porque es atómica.
    • Acechar: DeadMG cubiertos que un par de comentarios de arriba, es modificar como se describe por 1.10
    • El punto 1.10 contiene 25 párrafos. Lectura rápida a través de ellos yo no podía saber que se hace a explicar que una operación que no es una modificar puede incluir escribe otro que el bloqueo de los mutexes etc. Pero acepto que puedo tener sólo pasa por alto. Voy a seguir buscando, pero si tienes tiempo podrías por favor me apunte a la derecha del párrafo?
    • tal vez soy demasiado estúpido para hacerlo, pero necesito ayuda. p21 define los datos de «raza» y, en particular, los estados que no hay datos de la carrera cuando una de las acciones es atómica. Sin embargo, no dice que la acción atómica es no a modificar, mientras que STL párrafo prohíbe explícitamente modificaciones. Que es demasiado, creo. Sólo debe prohibir carreras de datos. Estoy escribiendo tonterías?
    • Acechar: creo que no he entendido tu comentario. La especificación no garantiza que no se crea razas, ni prohíbe que las modificaciones. Sólo se considera que escribe sólo puede ser introducido a través de llamadas en la no-const objetos, y si eso es cierto, entonces no va a introducir los datos de las carreras
    • Pero si usted escribe un sincronizado variable mutable, que es escribir. Y [17.6.5.9/3] dice explícitamente que «no podrá, directa o indirectamente, modificar«. Por cierto, ¿está bien para que usted pueda seguir aquí, o nos movemos en el chat? O usted no tiene tiempo para eso?
    • Acechar: yo no puedo darle toda mi atención en el momento, pero me gustaría continuar esta discusión en el chat el día de hoy.
    • es a las 2 de la mañana aquí, así que no sé cuánto tiempo voy a estar de vuelta. es allí una manera de dejarme un mensaje si no voy a estar aquí después? De todos modos, voy a aclarar mi punto en el siguiente comentario (espero que no hay espacio suficiente)
    • si lo tengo a la derecha, el punto es: «dentro de un const función que puede modificar un mutable variable, siempre y cuando usted hacer que atómicamente (es decir, con una adecuada sincronización)». Sin embargo, [17.6.5.9/3] dice: [una función STL] «no podrá, directa o indirectamente, modificar» [los argumentos por (entre otras cosas) llamar a uno de sus const funciones miembro]. Pero desde aquellos const funciones miembro puede modificar mutable variables, esto significa que la modificación de una mutable variable no se ve un «modificar» por [17.6.5.9/3] siempre que usted no es atómicamente. Y no puedo encontrar donde se indica.
    • A mí me parece que hay una lógica de la brecha. [17.6.5.9/3] prohíbe «demasiado» diciendo «que no podrá, directa o indirectamente modificar»; debe decir «no podrá, directa o indirectamente introducir los datos de la carrera», menos atómico de escribir en algún lugar está definido por no para ser un «modificar». Pero no puedo encontrar esto en cualquier lugar.
    • Acechar: creo que entiendo tu punto de vista… Modificaciones a través de const no están prohibidos, pero una implementación de la biblioteca estándar no serán aquellos con el fin de evitar carreras de datos (leído ese párrafo en el contexto del párrafo 1). Si eso no es suficiente, a continuación, su hasta usted para evitar que los datos de las carreras.
    • No sé, tanto como yo a entender el espíritu y el significado de todo lo que al final es lo que importa – todavía creo que técnicamente hay una lógica de la brecha en la redacción que le impide expresar ese significado. Incluso desde un punto de vista gramatical, la mera presencia de la número de párrafo «(1.10)» en [17.6.5.9/3] después de «modificar objetos» no es suficiente para calificar esa expresión como «modificar los objetos de de modo que introduzca los datos de la carrera como por (1.10.4)«, aunque esto es más probable que lo que se quiere decir.
    • Probablemente me hizo mi punto un poco más claro aquí: isocpp.org/blog/2012/12/… Gracias por intentar ayudar de todos modos.
    • a veces me pregunto quién fue el que (o los que están directamente involucrados) en realidad el responsable de escribir algunos párrafos estándar como estos.

Dejar respuesta

Please enter your comment!
Please enter your name here