2009-10-20 9 views
24

Estoy desarrollando un programa multihilo se ejecuta en Linux (compilado con G ++ 4.3) y si se busca por un poco encontrar una gran cantidad de historias de miedo sobre std :: string no ser seguro para subprocesos con GCC. Supuestamente, esto se debe al hecho de que internamente utiliza copia en escritura que causa estragos en herramientas como Helgrind.¿Está std :: string thead-safe con gcc 4.3?

He hecho un pequeño programa que copia una cadena a otra cadena y si se inspecciona las dos cadenas que ambos comparten el mismo puntero interno _M_p. Cuando se modifica una cadena, el puntero cambia, por lo que las cosas de copiar y escribir funcionan bien.

Lo que me preocupa, aunque es lo que sucede si comparto una cadena entre dos hilos (por ejemplo, pasando como un objeto en un coladatos multi-hilo entre dos hilos). Ya he intentado compilar con la opción '-pthread', pero eso no parece hacer mucha diferencia. Entonces mi pregunta:

  • ¿Hay alguna manera de forzar que std :: string sea seguro para la rosca? No me importaría si el comportamiento de copiado en escritura se deshabilitó para lograr esto.
  • ¿Cómo lo han resuelto otras personas? ¿O estoy siendo paranoico?

Me parece que no puede encontrar una respuesta definitiva, así que espero que ustedes me puede ayudar ..

Editar:

Wow, eso es un montón de respuestas en un corto tales hora. ¡Gracias! Definitivamente usaré la solución de Jack cuando quiera desactivar COW. Pero ahora la pregunta principal es: ¿realmente tengo que deshabilitar COW? ¿O es la 'contabilidad' hecha para el hilo VACA seguro? Actualmente estoy a navegar por las fuentes de libstdC++, pero eso va a tomar bastante tiempo para averiguar ...

Editar 2

OK navegado el código fuente libstdC++ y me parece algo como esto en libstd ++ - v3 /include/bits/basic_string.h:

_CharT* 
    _M_refcopy() throw() 
    { 
#ifndef _GLIBCXX_FULLY_DYNAMIC_STRING 
    if (__builtin_expect(this != &_S_empty_rep(), false)) 
#endif 
      __gnu_cxx::__atomic_add_dispatch(&this->_M_refcount, 1); 
    return _M_refdata(); 
    } // XXX MT 

Así que definitivamente hay algo ahí acerca de los cambios atómicos al contador de referencia ...

Conclusión

Estoy marcando el comentario de sellibitze como respuesta aquí porque creo que hemos llegado a la conclusión de que esta área aún no está resuelta por el momento. Para eludir el comportamiento de COW, sugeriría la respuesta de Jack Lloyd. ¡Gracias a todos por una discusión interesante!

+0

+1 buena pregunta! Desafortunadamente, las personas solo leen "threadsafe" y piensan "¡No!". ¡Ellos mejor leen toda la pregunta! :) – sellibitze

+0

Como std :: string es una instancia de la plantilla std :: basic_string, es posible que eche un vistazo al código fuente. Intente buscar cualquier macro que active/desactive la seguridad de los hilos. –

+0

Por cierto, copiar-en-escribir es lento en entornos de subprocesos múltiples, debe * querer * no usarlo, no estar dispuesto a hacerlo. – GManNickG

Respuesta

15

Los hilos aún no forman parte del estándar. Pero no creo que ningún proveedor pueda escaparse sin hacer que std :: string sea seguro para los hilos, hoy en día. Nota: Existen diferentes definiciones de "thread-safe" y las mías pueden diferir de las tuyas. Por supuesto, tiene poco sentido proteger un contenedor como std :: vector para el acceso simultáneo por defecto, incluso cuando no lo necesite. Eso iría en contra del espíritu de "no pagar por las cosas que no usas" de C++. El usuario siempre debe ser responsable de la sincronización si quiere compartir objetos entre diferentes hilos. El problema aquí es si un componente de la biblioteca utiliza y comparte algunas estructuras de datos ocultos que pueden conducir a carreras de datos, incluso si "las funciones se aplican a objetos diferentes" desde la perspectiva del usuario. El borrador de C++ 0x (N2960) contiene la sección "evitación de carrera de datos" que básicamente dice que los componentes de la biblioteca pueden acceder a los datos compartidos que están ocultos para el usuario si y solo si evita activamente posibles carreras de datos. Parece que una implementación de copia de escritura de std :: basic_string debe ser tan segura como w.r.t. multi-threading como otra implementación donde los datos internos nunca se comparten entre diferentes instancias de cadena.

No estoy 100% seguro de si libstdC++ ya se ocupa de eso. Creo que sí. Para estar seguro, echa un vistazo a the documentation

+1

Muchas gracias también por esa respuesta detallada. Reviso la página que vinculó, pero sigue siendo un poco vaga en mi opinión, hablando de contenedores en general (que debe proporcionar un bloqueo adecuado) y no tanto sobre cadenas. :) La evitación de raza de datos parece al menos una "excusa" para seguir adelante y suponer que todo estará bien entre hilos, ya que eso pone directamente la responsabilidad en las personas que implementan la biblioteca (siempre que yo, como programador, pase las cuerdas por valor en lugar de por referencia) ... – Benjamin

0

contenedor No STL es hilo de seguridad. De esta manera, la biblioteca tiene un propósito general (tanto para ser utilizado en el modo de subprocesamiento único como en el modo multi-subprocesamiento). En multihebra, deberá agregar el mecanismo de sincronización.

+3

Sin embargo, la diferencia con la cadena es que incluso pasarlo por valor induce problemas de enhebrado, esto puede ser algo inesperado, por decir lo menos. –

+1

¡Gracias a todos por sus respuestas rápidas!Sé que ningún contenedor STL es seguro para el hilo (y uso correctamente las envolturas de bloqueo cuando necesito que sean seguras para la fabricación de hilos) pero, por lo que sé, std :: string es el único que "secretamente" usa el mismo almacén de datos para cadenas idénticas. Mi preocupación es que la contabilidad de copiado no es segura para nada, ¿estoy correcto? – Benjamin

+9

No entiendo por qué esto obtiene tantos upvotes. Chicos, presten atención a la pregunta real – sellibitze

11

Si no le importa desactivación de copia en escritura, este puede ser el mejor curso de acción. std :: string COW solo funciona si sabe que está copiando otra std :: string, por lo que puede hacer que siempre asigne un nuevo bloque de memoria y realice una copia real. Por ejemplo, este código:

#include <string> 
#include <cstdio> 

int main() 
    { 
    std::string orig = "I'm the original!"; 
    std::string copy_cow = orig; 
    std::string copy_mem = orig.c_str(); 
    std::printf("%p %p %p\n", orig.data(), 
          copy_cow.data(), 
          copy_mem.data()); 
    } 

mostrará que la segunda copia (con c_str) evita COW.(Debido a que std :: string solo ve un const char * desnudo, y no tiene idea de dónde vino ni de su tiempo de vida, entonces tiene que hacer una nueva copia privada).

+12

Advertencia menor: la asignación al resultado de c_str() truncará la cadena si orig contiene nulos incrustados. Sería más seguro usar el método assign (o el constructor) tomando un const char * y un size_type, y pasar "orig.data()" y "orig.size()" a eso. –

+0

Si decido ir por la ruta 'desactivar VACA', definitivamente usaré esto (y la observación adicional de Eric). Bien hecho :) – Benjamin

+0

@Eric Excelente punto, no puedo creer que no haya considerado cómo eso interactuaría con los nulos. Gracias. –

0

Parece que este se fijó hace un tiempo: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=5444 fue (cerrado como el mismo problema que http://gcc.gnu.org/bugzilla/show_bug.cgi?id=5432, que se fijó en 3,1).

Ver también http://gcc.gnu.org/bugzilla/show_bug.cgi?id=6227

+0

Creo que el error se refiere al código iostreams, ¿no es el código de la cadena? – Benjamin

+0

Oh, correcto. Me referí a este ya que el que pertenece a basic_string (# 5444) se cerró con la misma resolución que 5432. Edité mi respuesta para aclarar esto. –

+0

Gracias Eric! Mientras agreguemos elementos de bugtracker, este también parece estar relacionado: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40518 – Benjamin

3

This sección del libstdC++ internos afirma:

El C++ funcionalidad cadena biblioteca requiere un par de operaciones atómicas para proporcionar hilos de seguridad. Si no realiza alguna acción especial, , la biblioteca usará versiones de código auxiliar de estas funciones que no son seguras para subprocesos. Funcionarán bien, a menos que las aplicaciones tengan varios subprocesos.

El recuento de referencias debe funcionar en un entorno de subprocesos múltiples. (A menos que su sistema no proporcione los elementos atómicos necesarios)

+0

Sería bueno si hubiera una manera rápida de saber si se usaron las funciones reales o de stub. Tal vez mire el código de la máquina y busque un prefijo LOCK ... –

0

De acuerdo con this bug issue, la implementación de copiar con escritura de std::basic_string aún no es totalmente segura para la ejecución de subprocesos. <ext/vstring.h> es una implementación sin COW y parece funcionar mucho mejor en un contexto de solo lectura.

Cuestiones relacionadas