2009-05-18 13 views
25

Necesito llamar a una función de la biblioteca que a veces no terminará dentro de un tiempo dado, desafortunadamente. ¿Hay alguna manera de llamar a la función pero cancelarla si no termina dentro de n segundos?C++: ¿Cómo implementar un tiempo de espera para una llamada de función arbitraria?

No puedo modificar la función, así que no puedo poner la condición de aborto en ella directamente. Tengo que agregar un tiempo de espera a la función externamente.

¿Es tal vez una posible solución para iniciarlo como un hilo (refuerzo), que luego puedo terminar después de un cierto tiempo? ¿Funcionaría algo así? De hecho, creo que la función es no thread-safe, pero eso no importaría si lo ejecuto como solo solo hilo, ¿verdad? ¿Hay otras (mejores) soluciones?

+0

¿Qué hace la función de la biblioteca? – peterchen

+0

Uuh, calcule algo ... Un cálculo científico que en realidad no terminará bajo ciertas condiciones. – Frank

Respuesta

18

Se podría generar una boost::thread a llamar a la API:

boost::thread api_caller(::api_function, arg1, arg2); 
if (api_caller.timed_join(boost::posix_time::milliseconds(500))) 
{ 
    // API call returned within 500ms 
} 
else 
{ 
    // API call timed out 
} 

Boost no permite que mates el subproceso de trabajo, sin embargo. En este ejemplo, es simplemente huérfano.

Deberá tener cuidado con lo que hace esa llamada API, ya que puede que nunca libere recursos adquiridos.

+1

Si api_function devuelve un valor, ¿cómo puedo acceder? –

+0

Este método crea un functor simple que llama a la API directamente. Para capturar el resultado, debe escribir su propio functor o lambda para asignar el código de retorno de la API a alguna variable a la que se pueda acceder desde otro lugar. En C++ 0x, podría escribir 'retcode RETTYPE; boost :: thread api_caller ([& retcode] (type1 arg1, type2 arg2) {retcode = :: api_function (arg1, arg2);}); ' –

8

Lo que está hablando se suele llamar un sistema de "vigilancia". El perro guardián generalmente es un segundo hilo que verifica el estado de todos los otros hilos. El perro guardián generalmente está configurado para ejecutarse periódicamente. Si no se ha recibido respuesta de los otros hilos, el perro guardián puede notificar al usuario, o incluso matar el hilo ofensivo si es posible hacerlo de manera segura (depende de su aplicación).

+4

Dependiendo de lo que esté haciendo la función de la biblioteca, matar el hilo que no responde podría ser muy inseguro. Un enfoque seguro sería enviar la tarea a un proceso separado. –

+0

¿Cómo comienzo un proceso tan separado? ¿Puede mi aplicación pasarle algunos datos (y recuperar algunos datos de resultados) sin pasar por el sistema de archivos? – Frank

+1

Eso es algo complicado, y específico de la plataforma; podría ser una pregunta separada de stackoverflow, creo. Hay varias formas de pasar datos entre procesos. Esto se llama IPC (Inter-Process Communication) y también es específico de la plataforma. –

3

El problema con los hilos es que algunos recursos no podrán liberarse después de la terminación del hilo. Si no adquiere los recursos que debe liberar, continúe con los hilos.

+2

Tenga en cuenta que también hay recursos que podrían adquirirse que no están bajo su control: puede llamar Las API que adquieren bloqueos y demás, y la terminación del hilo darán como resultado el huérfano de esos bloqueos. – Michael

14

Creo que la única manera segura de lograr esto sería engendrar un proceso separado sandbox que llame a la función de biblioteca como un proxy para su aplicación. Tendrá que implementar algún tipo de IPC entre su aplicación y el proxy. Implementar un tiempo de espera al leer la respuesta del IPC es bastante trivial. Si la lectura falla debido al tiempo de espera, puede finalizar el proxy de forma segura sin poner en riesgo el estado de su aplicación.

1

Lo que necesita es un hilo y un Future Object que puede contener el resultado de la llamada a la función.

Para ver un ejemplo con boost ver here.

Debería verificar el futuro después del tiempo de espera y, si no está configurado, actuar en consecuencia.

+1

Los futuros son geniales y a menudo útiles, pero no creo que resuelvan el problema el OP preguntó. Quiere ** detener la ejecución de la función ** si se ejecuta durante demasiado tiempo. –

+0

Bueno, podría tratar de terminar el hilo si el tiempo de espera expira, pero no es seguro como muchos otros han señalado. Si la función termina más tarde (a menos que esté rota), el futuro le permitirá continuar su programa principal después del tiempo de espera y simplemente ignorar el hilo y el futuro sabiendo que en algún momento en el futuro terminará. – lothar

3
+2

+1, idea interesante. Veo que el parámetro de tiempo de espera no funciona para Windows todavía. Además, los recursos utilizados por la función todavía se dejarán en un estado indeterminado (al menos sin asignar, y posiblemente en un estado intermedio no válido), por lo que no intente ejecutar su función más de una vez o según lo que toque. –

+0

Esta pregunta tiene un buen ejemplo de trabajo: [este]] (http://stackoverflow.com/questions/13690654/how-do-i-use-boost-execution-monitor-to-make-sure-if-my- function-get-hung-it-wi) – asanguinetti

3

El problema es que con una solución en proceso sin el apoyo de la función se termina con el estado podría no ser válido.

Ejemplo: Cuando finaliza el subproceso mientras se lleva a cabo una asignación de memoria, su montón de proceso puede estar dañado.

Así que puede finalizar la llamada, pero luego también debe finalizar el proceso. En muchos casos, las posibilidades de efectos secundarios destructivos son pequeñas, pero no apostaría mi cálculo a eso.

Puede, como sugiere Ben Straub, simplemente dejar el hilo huérfano: ponerlo en prioridad más baja y dejarlo funcionar para infinito. Esa es, por supuesto, solo una solución limitada: si el hilo consume recursos (probable), ralentizará el sistema, también hay un límite en los hilos por proceso (generalmente debido al espacio de direcciones para la pila de hilos).

En general, preferiría la solución de proceso externo. Un patrón simple es este:
Escribir datos de entrada en un archivo, inicie el proceso externo con el archivo como argumento. El proceso externo escribe el progreso (si lo hay) en un archivo de disco que se puede supervisar e incluso puede permitir que el proceso se reanude desde donde comenzó. Los resultados se escriben en el disco y el proceso principal puede leerlos.

Cuando finaliza el proceso, aún tiene que ocuparse de sincronizar el acceso a los recursos externos (como archivos) y cómo tratar con los mundos abandonados, la mitad -archivos escritos, etc. Pero generalmente es EL camino a una solución robusta.

1

Vaya con un proceso huérfano, ejecútelo y tiempo de ejecución. Si se queda sin tiempo, invoque el sistema operativo para matarlo.

Cómo evitar los concursos de carreras. en este patrón:

  • crear un archivo para almacenar en args (por supuesto, todo se pasa como VAL). El proceso huérfano solo puede leer datos de este archivo.

  • El huérfano procesa los datos de entrada, crea un archivo de salida con valores de resultado y lo cierra.

  • Solo cuando todo está hecho, huérfano borra el archivo de entrada, un hecho que señala el proceso maestro en el que se realizó el trabajo.

Esto evita la lectura de un medio escrito de problemas de archivos, ya que el maestro primeros avisos ausencia de archivo de entrada, se abre para leer el archivo de salida, lo que sin duda se ha completado (porque se cerró antes de borrar la entrada, y las pilas de llamadas OS son secuenciales).

1

"Necesito llamar a una función de la biblioteca que a veces no terminará dentro de un tiempo determinado, por desgracia. ¿Hay alguna manera de llamar a la función pero cancelarla si no termina en n segundos?"

La respuesta corta es no. Suele ser un problema ... La llamada en sí debe finalizar en algún momento (implementando su propio tiempo de espera), pero las llamadas de bloqueo generalmente son problemas (por ejemplo, gethostbyname()) porque depende del tiempo de espera (o del sistema), no del tuyo.

Por lo tanto, siempre que sea posible intente hacer que el código se ejecute en la salida del hilo limpiamente cuando sea necesario; el código en sí debe detectar y manejar el error. Puede enviar un mensaje y/o establecer estados para que el hilo principal (u otro) sepa lo que sucedió.

Preferencia personal, en sistemas de alta disponibilidad, me gusta que mis hilos giren a menudo (sin bloqueo de ocupado) con tiempos de espera específicos, llamando a funciones sin bloqueo y con condiciones de salida precisas. Una variable global 'específica' o 'específica' especifica el truco para una salida limpia.

Cuestiones relacionadas