¿Cuáles son las tensiones entre multiproceso y excepción-seguridad en C++? ¿Hay buenas pautas a seguir? ¿Termina un hilo debido a una excepción no detectada?Escritura de código seguro de excepciones multiproceso
Respuesta
Creo que el estándar C++ no menciona el multihilo: el multihilo es una característica específica de la plataforma.
No estoy exactamente seguro de lo que dice el estándar C++ sobre excepciones no detectadas en general, pero de acuerdo con this page, lo que ocurre es definición de plataforma, y debe encontrarlo en la documentación del compilador.
En una prueba rápida y sucia hice con g ++ 4.0.1 (i686-apple-darwin8-g ++ - 4.0.1 para ser específico), el resultado es que se llama terminate()
, lo que mata a todo el programa. El código utilicé la siguiente manera:
#include <stdio.h>
#include <pthread.h>
void *threadproc(void *x)
{
throw 0;
return NULL;
}
int main(int argc, char **argv)
{
pthread_t t;
pthread_create(&t, NULL, threadproc, NULL);
void *ret;
pthread_join(t, &ret);
printf("ret = 0x%08x\n", ret);
return 0;
}
compilado con g++ threadtest.cc -lpthread -o threadtest
. La salida fue:
terminate called after throwing an instance of 'int'
No recomiendo dejar ninguna excepción sin capturar. Envuelva las funciones de subproceso de nivel superior en controladores de catch-all que pueden cerrar el programa de forma más elegante (o al menos exhaustiva).
Aunque esto * puede * tener sentido para algunas aplicaciones, no estoy de acuerdo con él como consejo general. Por lo general, recomiendo que las personas manejen las excepciones cuando tenga sentido, de lo contrario, que se propaguen. Si usó RAII correctamente, apagarlo no debería ser un problema. – MattyT
Excepto que sus destructores de RAII pueden no ser llamados si la excepción no se detecta. Depende de la implementación si una excepción no detectada desenrolla la pila o no. Simplemente puede abortar. Si atrapas todo en la parte superior, garantizas que la pila se desenrolla. –
@onebyone: ¿Según qué párrafo de la especificación? Solo quiero saber que todos los gurús de C++ (Herb Sutter, etc.) recomiendan escribir código que no use try/catch, sino que sea "exception-agnostic". –
Creo que lo más importante es recordar que las excepciones no detectadas de otros hilos no se muestran al usuario o se lanzan al hilo principal. Entonces, debe urdir todo el código que debe ejecutarse en hilos diferentes al hilo principal con los bloques try/catch.
Una excepción no detectada llamará al terminate()
que a su vez llama al terminate_handler
(que puede ser configurado por el programa). Por defecto, el terminate_handler
llama al abort()
.
Incluso si anula el valor predeterminado terminate_handler
, la norma dice que la rutina que proporcione "terminará la ejecución del programa sin regresar a la persona que llama" (ISO 14882-2003 18.6.1.3).
Por lo tanto, en resumen, una excepción no detectada terminará el programa, no solo el hilo.
En lo que respecta a la seguridad de los hilos, como dice Adam Rosenfield, esta es una cuestión específica de la plataforma que no se aborda en el estándar.
Esta es la principal razón por la que existe Erlang.
No sé cuál es la convención, pero en mi humilde opinión, sé lo más parecido posible a Erlang. Haga que los objetos de montón sean inmutables y configure algún tipo de protocolo de paso de mensajes para comunicarse entre hilos. Evita los bloqueos. Asegúrese de que el mensaje que pasa sea a prueba de excepciones. Mantenga tantas cosas con estado en la pila.
Como han discutido otros, la concurrencia (y la seguridad de subprocesos en particular) es un problema arquitectónico que afecta la forma en que diseña su sistema y su aplicación.
Pero me gustaría responder a su pregunta sobre la tensión entre excepción-seguridad e hilo de seguridad.
En el nivel de clase, la seguridad de los hilos requiere cambios en la interfaz. Al igual que la excepción, la seguridad sí.Por ejemplo, es habitual que las clases para volver referencias a variables internas, por ejemplo:
class Foo {
public:
void set_value(std::string const & s);
std::string const & value() const;
};
Si Foo es compartida por múltiples hilos, problemas le espera. Naturalmente, podría poner un mutex u otro bloqueo para acceder a Foo. Pero pronto, todos los programadores de C++ querrían envolver a Foo en un "ThreadSafeFoo". Mi opinión, es que la interfaz de Foo debe ser cambiado a:
class Foo {
public:
void set_value(std::string const & s);
std::string value() const;
};
Sí, es más caro, pero se puede hacer seguro para subprocesos con las cerraduras dentro de Foo. IMnsHO esto crea una cierta cantidad de tensión entre la seguridad de la rosca y la seguridad de la excepción. O al menos, necesita realizar más análisis ya que cada clase utilizada como recurso compartido debe examinarse bajo ambas luces.
Hay dos cuestiones que me di cuenta:
en g ++ en Linux, el asesinato de un hilo (pthread_cancel) se realiza mediante una excepción "desconocido". Por un lado, eso te permite limpiar bien cuando se está matando el hilo. Por otro lado, si detecta esa excepción y no la vuelve a lanzar, su código termina con abort(). Por lo tanto, si usted o cualquiera de las bibliotecas que utiliza hilos de matar, no se puede tener
captura (...)
sin
throw;
en el código roscado. Here es una referencia a este comportamiento en la web:
- A veces es necesario transferir la excepción entre subprocesos. Esto no es algo fácil de hacer, terminamos haciendo algo, cuando la solución adecuada es el tipo de marshalling/demarshalling que usarías entre los procesos.
C++ 0x tendrá Language Support for Transporting Exceptions between Threads de modo que cuando un hilo de trabajo arroje una excepción, el hilo de desove pueda atraparlo o volver a lanzarlo.
partir de la propuesta:
namespace std {
typedef unspecified exception_ptr;
exception_ptr current_exception();
void rethrow_exception(exception_ptr p);
template< class E > exception_ptr copy_exception(E e);
}
Tenga en cuenta que esta funcionalidad ya existe en boost. –
Para obtener más información sobre la implementación de Boost: http://www.boost.org/doc/libs/release/libs/exception/doc/tutorial_exception_ptr.html –
Un ejemplo clásico (no recuerdo donde vi por primera vez) se encuentra en la biblioteca std.
Así es como usted hace estallar algo de una cola:
T t;
t = q.front(); // may throw
q.pop();
Esta interfaz es algo obtuso en comparación con:
T t = q.pop();
Pero se hace porque la asignación copia T puede lanzar. Si la copia se lanza después de que ocurre el pop, ese elemento se pierde de la cola y nunca se puede recuperar. Pero dado que la copia ocurre antes de que el elemento aparezca, puede poner un manejo arbitrario alrededor de la copia desde el frente() en los bloques try/catch.
La desventaja es que no puede implementar una cola segura para subprocesos con la interfaz de std :: queue debido a los dos pasos implicados. Lo que es bueno para la seguridad de excepciones (separar los pasos que se pueden lanzar), ahora es malo para el multihilo.
Su salvador principal en cuanto a la seguridad de las excepciones es que las operaciones del puntero no son de tiro. De forma similar, las operaciones de puntero se pueden hacer atómicas en la mayoría de las plataformas, por lo que a menudo pueden ser su salvador en código multiproceso. Puedes tener tu pastel y comértelo también, pero es muy difícil.
No entendí cómo concluyó "no se puede implementar una cola que es seguro para subprocesos con la interfaz de std :: queue debido a los dos pasos involucrados ". –
Piénselo: incluso si bloquea el acceso a la estructura de datos interna, porque ese bloqueo no se mantiene entre las llamadas al frente y al pop, después de obtener el elemento frontal, otro hilo también podría obtener el mismo elemento frontal antes que usted Apagarlo, luego pierde un elemento y un elemento se duplica. –
- 1. Directrices para probar el código multiproceso o asegurarse de que el código sea seguro para subprocesos
- 2. Escritura código de mantenimiento
- 3. ¿Este código es realmente multiproceso?
- 4. ¿Ejemplos de buen código Java multiproceso?
- 5. firma automática de código "seguro"
- 6. Escribir código de cacao seguro
- 7. ¿Por qué usar el manejo de excepciones en un código aparentemente "seguro"?
- 8. Herramientas para visualizar el grafo de llamada de aplicación C++ multiproceso, cobertura de código multiproceso?
- 9. No estoy seguro de cuándo usar excepciones en C++
- 10. Código seguro contra inseguro
- 11. Código Asamblea de escritura para un tatuaje
- 12. Diferencia de rendimiento para multiproceso y multiproceso
- 13. Diseño multiproceso de Python
- 14. ¿No es seguro lanzar excepciones desde librerías C++ enlazadas estáticamente?
- 15. Problemas de cola .NET multiproceso
- 16. Código python más seguro ejecutando
- 17. Código no seguro en C#
- 18. Código de ejemplo para manejar Excepciones
- 19. Cómo escribir código seguro/correcto de subprocesos múltiples en .NET?
- 20. Ejecución de código serie en un programa multiproceso en C++
- 21. PHPUnit Cobertura y excepciones Código
- 22. Aplicaciones multiproceso
- 23. Java NIO SocketChannel.read() con multiproceso
- 24. Prueba de unidades multiproceso
- 25. manejo de señales multiproceso
- 26. Ejemplo multiproceso de Cherrypy
- 27. Gestión de montón multiproceso
- 28. tarea de rake multiproceso
- 29. Patrón de observador multiproceso
- 30. Servicio de Windows multiproceso en MingW
Iba a escribir lo mismo. Simplemente deje en claro que la aplicación terminará (alguien puede interpretar que solo el hilo se acaba con terminación). terminate() hará que todos los hilos se eliminen sin que se produzca ningún otro procesamiento. –
PD. Este es el comportamiento de todas las bibliotecas pthread que he usado. –