Pregunta std :: string en un programa multiproceso


Dado que:

1) El estándar C ++ 03 no aborda la existencia de hilos de ninguna manera

2) El estándar C ++ 03 lo deja en manos de las implementaciones para decidir si std::string debería usar la semántica Copiar-en-Escribir en su copia-constructor

3) La semántica de copiar y escribir a menudo conduce a un comportamiento impredecible en un programa de subprocesos múltiples

Llego a la siguiente conclusión, aparentemente controvertida:

Simplemente no puede usar de manera segura y portátil std :: string en un programa multiproceso

Obviamente, ninguna estructura de datos STL es segura para subprocesos. Pero al menos, con std :: vector, por ejemplo, puede simplemente usar mutexes para proteger el acceso al vector. Con una implementación de std :: string que utiliza COW, ni siquiera se puede hacer de manera confiable sin editar la semántica de recuento de referencias en profundidad dentro de la implementación del proveedor.

Ejemplo del mundo real:

En mi compañía, tenemos una aplicación de múltiples subprocesos que ha sido probada exhaustivamente y validada a través de Valgrind en innumerables ocasiones. La aplicación se ejecutó durante meses sin problemas de ningún tipo. Un día, recompilo la aplicación en otra versión de gcc, y de repente obtengo segfaults al azar todo el tiempo. Valgrind ahora informa sobre accesos de memoria no válidos en profundidad dentro de libstdc ++, en el constructor de copia std :: string.

¿Entonces, cuál es la solución? Bueno, por supuesto, podría typedef std::vector<char> como una clase de cuerdas, pero realmente, eso apesta. También podría esperar C ++ 0x, y pido que los implementadores renuncien a COW. O, (estremecimiento), podría usar una clase de cuerda personalizada. Personalmente siempre enfurezco a los desarrolladores que implementan sus propias clases cuando una biblioteca preexistente funciona bien, pero, sinceramente, necesito una clase de cadena de caracteres que estoy seguro de que no está utilizando la semántica de COW; y std :: string simplemente no garantiza eso.

Estoy en lo cierto que std::string simplemente no se puede usar confiablemente en absoluto en programas portátiles con múltiples subprocesos? ¿Y cuál es una buena solución?


32
2017-11-02 12:55


origen


Respuestas:


Dado que el estándar no dice una palabra acerca de los modelos de memoria y es completamente inadvertido, diría que definitivamente no se puede asumir que cada implementación será no vaca, entonces no, no se puede

Además de eso, si conoce sus herramientas, la mayoría de las implementaciones usarán cadenas que no sean cow para permitir el multi-threading.


4
2017-11-02 13:05



No puede hacer nada de manera segura y portátil en un programa de subprocesos múltiples. No hay tal cosa como un programa portable C ++ de subprocesos múltiples, precisamente porque los hilos arrojan todo lo que dice C ++ sobre el orden de las operaciones y los resultados de modificar cualquier variable, fuera de la ventana.

Tampoco hay nada en el estándar para garantizar que vector puede ser usado en la forma en que dices. Sería legal proporcionar una implementación de C ++ con una extensión de subprocesamiento en la que, por ejemplo, cualquier uso de un vector fuera del subproceso en el que se inicializó da como resultado un comportamiento indefinido. En el instante en que inicia un segundo hilo, ya no usa C ++ estándar, y debe buscar en el proveedor del compilador lo que es seguro y lo que no.

Si su proveedor proporciona una extensión de subprocesamiento, y también proporciona una cadena estándar con COW que (por lo tanto) no se puede convertir en un subproceso seguro, entonces, por el momento, su argumento es con su proveedor, o con la extensión de subprocesamiento, no con el estándar C ++. Por ejemplo, podría decirse que POSIX debería haber excluido cadenas COW en programas que usan pthreads.

Podrías asegurarte teniendo un único mutex, que tomas mientras haces cualquier mutación de cadena, y cualquier lectura de una cadena que sea el resultado de una copia. Pero probablemente tendrías una contienda paralizante en ese mutex.


11
2017-11-02 13:11



Tienes razón. Esto se solucionará en C ++ 0x. Por ahora, debe confiar en la documentación de su implementación. Por ejemplo, versiones libstdc ++ recientes (GCC) le permiten usar objetos de cadena como si ningún objeto de cadena comparte su búfer con otro. C ++ 0x obliga a la implementación de una biblioteca a proteger al usuario del "intercambio oculto".


8
2017-12-23 12:16



Una forma más correcta de verlo sería "No se puede usar C + + de manera segura y portátil en un entorno multiproceso". No hay garantía de que otras estructuras de datos se comporten con sensatez tampoco. O que el tiempo de ejecución no hará estallar tu computadora. El estándar no garantiza cualquier cosa sobre hilos

Entonces para hacer cualquier cosa con hilos en C ++, debe confiar en las garantías definidas por la implementación. Y luego puedes usar de forma segura std::string porque cada implementación le dice si es seguro usarlo en un entorno enhebrado.

Perdió toda esperanza de verdadera portabilidad en el momento en que engendró un segundo hilo. std::string no es "menos portátil" que el resto del idioma / biblioteca.


4
2017-12-21 14:32



Puedes usar STLport. Proporciona cadenas que no son COW. Y tiene el mismo comportamiento en diferentes plataformas.

Esta artículo presenta la comparación de cadenas STL con copy-on-write y noncopy- argorithms en escritura, basados ​​en cadenas STLport, cuerdas y GNU libstdc ++ implementaciones.

En una empresa donde trabajo tengo cierta experiencia ejecutando la misma aplicación de servidor construida con STLport y sin STLport en HP-UX 11.31. La aplicación fue compilada con gcc 4.3.1 con nivel de optimización O2. Así que cuando ejecuto el programa construido con STLport procesa las solicitudes un 25% más rápido en comparación con el mismo programa construido sin STLport (que usa la propia biblioteca STL de gcc).

Hice un perfil de ambas versiones y descubrí que la versión sin STLport pasa mucho más tiempo en pthread_mutex_unlock() (2.5%) en comparación con la versión con STLport (1%). Y pthread_mutex_unlock() en sí mismo en la versión sin STLport se llama desde una de las funciones std :: string.

Sin embargo, cuando después del perfilado cambié las asignaciones a las cadenas en las funciones más comúnmente llamadas de esta manera:

string_var = string_var.c_str(); // added .c_str()

hubo una mejora significativa en el rendimiento de la versión sin STLport.


2
2017-11-02 13:04



Regulo el acceso de cadena:

  • hacer std::string miembros privados
  • regreso const std::string& para getters
  • Setters modifican el miembro

Esto siempre funcionó bien para mí y es la ocultación correcta de datos.


0
2017-12-21 14:35



En MSVC, std :: string ya no es un puntero compartido contado de referencia en un contenedor. Eligen el valor por valor de todos los contenidos en cada constructor de copias y operador de asignación, para evitar problemas de subprocesamiento múltiple.


0
2017-11-02 17:18