C ++ - faq generalmente es administrado por la comunidad de C ++, y podría venir y pedirnos opiniones en nuestro chat.
Puppy
@DeadMG: No estaba al tanto de C ++ - faq y su etiqueta, se sugirió en un comentario.
K-Ballo
2
¿Dónde escuchaste que const significa seguro para subprocesos?
Mark B
2
@Mark B: Herb Sutter y Bjarne Stroustrup lo decían en Standard C ++ Foundation , vea el enlace al final de la respuesta.
K-Ballo
NOTA PARA LOS QUE VIENEN AQUÍ: la verdadera pregunta NO es si constsignifica seguro para subprocesos. Eso sería una tontería, ya que de lo contrario significaría que debería poder seguir adelante y marcar cada método seguro para subprocesos como const. Más bien, la pregunta que realmente estamos haciendo es constIMPLICA seguridad para subprocesos, y de eso se trata esta discusión.
user541686
Respuestas:
131
Escuché que eso constsignifica seguro para subprocesos en C ++ 11 . ¿Es eso cierto?
Es algo cierto ...
Esto es lo que tiene que decir el lenguaje estándar sobre seguridad de subprocesos:
[1.10 / 4]
Dos evaluaciones de expresión entran en conflicto si una de ellas modifica una ubicación de memoria (1.7) y la otra accede o modifica la misma ubicación de memoria.
[1.10 / 21]
La ejecución de un programa contiene una carrera de datos si contiene dos acciones en conflicto en diferentes subprocesos, al menos una de las cuales no es atómica, y ninguna ocurre antes que la otra. Cualquier carrera de datos de este tipo da como resultado un comportamiento indefinido.
que no es más que la condición suficiente para que se produzca una carrera de datos :
Se están realizando dos o más acciones al mismo tiempo en una cosa determinada; y
Al menos uno de ellos es escrito.
La biblioteca estándar se basa en eso, yendo un poco más allá:
[17.6.5.9/1]
Esta sección especifica los requisitos que deben cumplir las implementaciones para evitar carreras de datos (1.10). Cada función de biblioteca estándar debe cumplir con cada requisito a menos que se especifique lo contrario. Las implementaciones pueden evitar carreras de datos en casos distintos a los que se especifican a continuación.
[17.6.5.9/3]
Una función de biblioteca estándar de C ++ no modificará directa o indirectamente objetos (1.10) accesibles por subprocesos distintos del subproceso actual a menos que se acceda a los objetos directa o indirectamente a través de losargumentosno constantes de la función, incluidosthis.
que en palabras simples dice que espera que las operaciones en constobjetos sean seguras para subprocesos . Esto significa que la biblioteca estándar no introducirá una carrera de datos siempre que las operaciones en constobjetos de su propio tipo tampoco
Consiste íntegramente en lecturas --es decir, no hay escrituras--; o
Sincroniza internamente escrituras.
Si esta expectativa no es válida para uno de sus tipos, usarlo directa o indirectamente junto con cualquier componente de la Biblioteca estándar puede resultar en una carrera de datos . En conclusión, constsignifica seguro para subprocesos desde el punto de vista de la biblioteca estándar . Es importante tener en cuenta que esto es simplemente un contrato y el compilador no lo hará cumplir, si lo rompe, obtendrá un comportamiento indefinido y estará solo. Si constestá presente o no, no afectará la generación de código, al menos no con respecto a las carreras de datos .
Significa eso constestá ahora el equivalente de Java s' synchronized?
No se . De ningún modo...
Considere la siguiente clase demasiado simplificada que representa un rectángulo:
La función miembroarea es segura para subprocesos ; no porque sea const, sino porque consiste enteramente en operaciones de lectura. No hay escrituras involucradas, y al menos una escritura involucrada es necesaria para que ocurra una carrera de datos . Eso significa que puede llamar areadesde tantos hilos como desee y obtendrá resultados correctos todo el tiempo.
Tenga en cuenta que esto no significa que rectsea seguro para subprocesos . De hecho, es fácil ver cómo si areaocurriera una llamada a al mismo tiempo que una llamada a set_sizeen un determinado rect, entonces areapodría terminar calculando su resultado basado en un ancho anterior y una nueva altura (o incluso en valores confusos) .
Pero eso está bien, rectno es constasí que ni siquiera se espera que sea seguro para subprocesos después de todo. Un objeto declarado const rect, por otro lado, sería seguro para subprocesos ya que no es posible escribir (y si está considerando const_cast-ing algo originalmente declarado, constentonces obtiene un comportamiento indefinido y eso es todo).
Entonces, ¿qué significa eso?
Supongamos, por el bien del argumento, que las operaciones de multiplicación son extremadamente costosas y es mejor evitarlas cuando sea posible. Podríamos calcular el área solo si se solicita, y luego almacenarla en caché en caso de que se solicite nuevamente en el futuro:
[Si este ejemplo parece demasiado artificial, podría reemplazarlo mentalmente intpor un entero muy grande asignado dinámicamente que no es inherentemente seguro para subprocesos y para el cual las multiplicaciones son extremadamente costosas].
La función miembroarea ya no es segura para subprocesos , está escribiendo ahora y no está sincronizada internamente. ¿Es un problema? La llamada a areapuede ocurrir como parte de un constructor de copia de otro objeto, tal constructor podría haber sido llamado por alguna operación en un contenedor estándar , y en ese punto la biblioteca estándar espera que esta operación se comporte como una lectura con respecto a las carreras de datos. . ¡Pero estamos escribiendo!
Tan pronto como colocamos un recten un contenedor estándar, directa o indirectamente, estamos firmando un contrato con la Biblioteca estándar . Para seguir haciendo escrituras en una constfunción sin dejar de cumplir con ese contrato, necesitamos sincronizar internamente esas escrituras:
Tenga en cuenta que hicimos la areafunción segura para subprocesos , pero recttodavía no es segura para subprocesos . Un llamado a areasuceder al mismo tiempo que una llamada a set_sizetodavía puede llegar a calcular el valor erróneo, ya que las asignaciones a widthy heightno están protegidos por el mutex.
Si realmente quisiéramos un hilo segurorect , usaríamos una primitiva de sincronización para proteger el no seguro para hilosrect .
¿Se están quedando sin palabras clave ?
Sí lo son. Se han estado quedando sin palabras clave desde el primer día.
@Ben Voigt: Tengo entendido que la especificación C ++ 11 para std::stringestá redactada de una manera que ya prohíbe COW . Sin embargo, no recuerdo los detalles ...
K-Ballo
3
@BenVoigt: No. Simplemente evitaría que tales cosas no estén sincronizadas, es decir, no sean seguras para subprocesos. C ++ 11 ya prohíbe explícitamente COW; sin embargo, este pasaje en particular no tiene nada que ver con eso y no prohibiría COW.
Puppy
2
Me parece que hay una brecha lógica. [17.6.5.9/3] prohíbe "demasiado" al decir "no modificará directa o indirectamente"; debería decir "no introducirá directa o indirectamente una carrera de datos", a menos que una escritura atómica esté definida en algún lugar para no ser una "modificación". Pero no puedo encontrar esto en ningún lado.
Andy Prowl
1
Probablemente dejé todo mi punto un poco más claro aquí: isocpp.org/blog/2012/12/… Gracias por intentar ayudar de todos modos.
Andy Prowl
1
a veces me pregunto quién fue el (o los directamente involucrados) realmente responsable de escribir algunos párrafos estándar como estos.
const
significa seguro para subprocesos. Eso sería una tontería, ya que de lo contrario significaría que debería poder seguir adelante y marcar cada método seguro para subprocesos comoconst
. Más bien, la pregunta que realmente estamos haciendo esconst
IMPLICA seguridad para subprocesos, y de eso se trata esta discusión.Respuestas:
Es algo cierto ...
Esto es lo que tiene que decir el lenguaje estándar sobre seguridad de subprocesos:
que no es más que la condición suficiente para que se produzca una carrera de datos :
La biblioteca estándar se basa en eso, yendo un poco más allá:
que en palabras simples dice que espera que las operaciones en
const
objetos sean seguras para subprocesos . Esto significa que la biblioteca estándar no introducirá una carrera de datos siempre que las operaciones enconst
objetos de su propio tipo tampocoSi esta expectativa no es válida para uno de sus tipos, usarlo directa o indirectamente junto con cualquier componente de la Biblioteca estándar puede resultar en una carrera de datos . En conclusión,
const
significa seguro para subprocesos desde el punto de vista de la biblioteca estándar . Es importante tener en cuenta que esto es simplemente un contrato y el compilador no lo hará cumplir, si lo rompe, obtendrá un comportamiento indefinido y estará solo. Siconst
está presente o no, no afectará la generación de código, al menos no con respecto a las carreras de datos .No se . De ningún modo...
Considere la siguiente clase demasiado simplificada que representa un rectángulo:
La función miembro
area
es segura para subprocesos ; no porque seaconst
, sino porque consiste enteramente en operaciones de lectura. No hay escrituras involucradas, y al menos una escritura involucrada es necesaria para que ocurra una carrera de datos . Eso significa que puede llamararea
desde tantos hilos como desee y obtendrá resultados correctos todo el tiempo.Tenga en cuenta que esto no significa que
rect
sea seguro para subprocesos . De hecho, es fácil ver cómo siarea
ocurriera una llamada a al mismo tiempo que una llamada aset_size
en un determinadorect
, entoncesarea
podría terminar calculando su resultado basado en un ancho anterior y una nueva altura (o incluso en valores confusos) .Pero eso está bien,
rect
no esconst
así que ni siquiera se espera que sea seguro para subprocesos después de todo. Un objeto declaradoconst rect
, por otro lado, sería seguro para subprocesos ya que no es posible escribir (y si está considerandoconst_cast
-ing algo originalmente declarado,const
entonces obtiene un comportamiento indefinido y eso es todo).Supongamos, por el bien del argumento, que las operaciones de multiplicación son extremadamente costosas y es mejor evitarlas cuando sea posible. Podríamos calcular el área solo si se solicita, y luego almacenarla en caché en caso de que se solicite nuevamente en el futuro:
[Si este ejemplo parece demasiado artificial, podría reemplazarlo mentalmente
int
por un entero muy grande asignado dinámicamente que no es inherentemente seguro para subprocesos y para el cual las multiplicaciones son extremadamente costosas].La función miembro
area
ya no es segura para subprocesos , está escribiendo ahora y no está sincronizada internamente. ¿Es un problema? La llamada aarea
puede ocurrir como parte de un constructor de copia de otro objeto, tal constructor podría haber sido llamado por alguna operación en un contenedor estándar , y en ese punto la biblioteca estándar espera que esta operación se comporte como una lectura con respecto a las carreras de datos. . ¡Pero estamos escribiendo!Tan pronto como colocamos un
rect
en un contenedor estándar, directa o indirectamente, estamos firmando un contrato con la Biblioteca estándar . Para seguir haciendo escrituras en unaconst
función sin dejar de cumplir con ese contrato, necesitamos sincronizar internamente esas escrituras:Tenga en cuenta que hicimos la
area
función segura para subprocesos , perorect
todavía no es segura para subprocesos . Un llamado aarea
suceder al mismo tiempo que una llamada aset_size
todavía puede llegar a calcular el valor erróneo, ya que las asignaciones awidth
yheight
no están protegidos por el mutex.Si realmente quisiéramos un hilo seguro
rect
, usaríamos una primitiva de sincronización para proteger el no seguro para hilosrect
.Sí lo son. Se han estado quedando sin palabras clave desde el primer día.
Fuente : No lo sabes
const
ymutable
- Herb Sutterfuente
std::string
está redactada de una manera que ya prohíbe COW . Sin embargo, no recuerdo los detalles ...