java - ropa - envolver regalo tipo bolsa
¿Cómo puedo envolver un método para que pueda matar su ejecución si excede un tiempo de espera especificado? (7)
Tengo un método que me gustaría llamar. Sin embargo, estoy buscando una manera limpia y simple de matarlo o forzarlo a regresar si tarda demasiado en ejecutarse.
Estoy usando Java.
para ilustrar:
logger.info("sequentially executing all batches...");
for (TestExecutor executor : builder.getExecutors()) {
logger.info("executing batch...");
executor.execute();
}
Me imagino que la clase TestExecutor
debería implement Callable
y continuar en esa dirección.
Pero todo lo que quiero hacer es detener executor.execute()
si tarda demasiado.
Sugerencias ...?
EDITAR
Muchas de las sugerencias recibidas asumen que el método que se está ejecutando requiere mucho tiempo y contiene algún tipo de bucle y que una variable se puede verificar periódicamente. Sin embargo, éste no es el caso. Entonces, algo que no necesariamente estará limpio y que simplemente detendrá la ejecución donde sea es aceptable.
Asumo el uso de múltiples hilos en las siguientes afirmaciones.
He leído algo en esta área y la mayoría de los autores dicen que es una mala idea matar a otro hilo.
Si la función que desea eliminar puede diseñarse para verificar periódicamente una variable o primitiva de sincronización, y luego terminar de forma limpia si esa variable o primitiva de sincronización está configurada, eso sería bastante limpio. Entonces, algún tipo de subproceso de monitor puede dormir durante varios milisegundos y luego establecer la variable o primitiva de sincronización.
Deberías echarle un vistazo a estas clases: FutureTask , Callable , Executors
Aquí hay un ejemplo :
public class TimeoutExample {
public static Object myMethod() {
// does your thing and taking a long time to execute
return someResult;
}
public static void main(final String[] args) {
Callable<Object> callable = new Callable<Object>() {
public Object call() throws Exception {
return myMethod();
}
};
ExecutorService executorService = Executors.newCachedThreadPool();
Future<Object> task = executorService.submit(callable);
try {
// ok, wait for 30 seconds max
Object result = task.get(30, TimeUnit.SECONDS);
System.out.println("Finished with result: " + result);
} catch (ExecutionException e) {
throw new RuntimeException(e);
} catch (TimeoutException e) {
System.out.println("timeout...");
} catch (InterruptedException e) {
System.out.println("interrupted");
}
}
}
El mecanismo de interrupción de Java está diseñado para este tipo de escenario. Si el método que desea abortar es ejecutar un bucle, simplemente haga que verifique el estado interrumpido de la secuencia en cada iteración. Si se interrumpe, lanza una InterruptedException.
Luego, cuando quieras abortar, solo tienes que invocar la interrupción en el hilo apropiado.
Alternativamente, puede usar el enfoque sugerido por Sun como una alternativa al método de parada en desuso. Esto no implica arrojar ninguna excepción, el método simplemente volvería normalmente.
En realidad, no se puede ... La única forma de hacerlo es utilizar thread.stop, acordar un método "cooperativo" (por ejemplo, verificar ocasionalmente el Thread.isinterrupted o llamar a un método que arroje una excepción InterruptedException, por ejemplo, Thread. sleep ()), o de alguna manera invocar el método en otra JVM por completo.
Para ciertos tipos de pruebas, llamar a stop () está bien, pero probablemente dañará el estado de su conjunto de pruebas, por lo que deberá reiniciar la JVM después de cada llamada para detener () si desea evitar los efectos de interacción.
Para obtener una buena descripción de cómo implementar el enfoque cooperativo, consulte las preguntas frecuentes de Sun sobre los métodos de subprocesamiento en desuso .
Para un ejemplo de este enfoque en la vida real, el objeto ''IProgressMonitor'' de Job API de Eclipse RCP permite que algunos servicios de gestión señalicen subprocesos (a través del método ''cancelar'') que deben detener. Por supuesto, eso depende de los métodos para verificar realmente el método isCancelled con regularidad, que a menudo no pueden hacer.
Un enfoque híbrido podría ser preguntar el hilo de manera agradable con la interrupción, luego insistir un par de segundos más tarde con la parada. De nuevo, no debería usar detenerse en el código de producción, pero podría estar bien en este caso, esp. si sale de la JVM poco después.
Para probar este enfoque, escribí un arnés simple, que toma un ejecutable e intenta ejecutarlo. Siéntase libre de comentar / editar.
public void testStop(Runnable r) {
Thread t = new Thread(r);
t.start();
try {
t.join(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (!t.isAlive()) {
System.err.println("Finished on time.");
return;
}
try {
t.interrupt();
t.join(2000);
if (!t.isAlive()) {
System.err.println("cooperative stop");
return;
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.err.println("non-cooperative stop");
StackTraceElement[] trace = Thread.getAllStackTraces().get(t);
if (null != trace) {
Throwable temp = new Throwable();
temp.setStackTrace(trace);
temp.printStackTrace();
}
t.stop();
System.err.println("stopped non-cooperative thread");
}
Para probarlo, escribí dos circuitos infinitos competidores, uno cooperativo y uno que nunca verifica el bit interrumpido de su hilo.
public void cooperative() {
try {
for (;;) {
Thread.sleep(500);
}
} catch (InterruptedException e) {
System.err.println("cooperative() interrupted");
} finally {
System.err.println("cooperative() finally");
}
}
public void noncooperative() {
try {
for (;;) {
Thread.yield();
}
} finally {
System.err.println("noncooperative() finally");
}
}
Finalmente, escribí las pruebas (JUnit 4) para ejercitarlas:
@Test
public void testStopCooperative() {
testStop(new Runnable() {
@Override
public void run() {
cooperative();
}
});
}
@Test
public void testStopNoncooperative() {
testStop(new Runnable() {
@Override
public void run() {
noncooperative();
}
});
}
Nunca antes había usado Thread.stop (), así que no tenía conocimiento de su funcionamiento. Funciona lanzando un objeto ThreadDeath desde donde se está ejecutando el hilo de destino. Esto extiende el error. Entonces, si bien no siempre funciona limpiamente, generalmente dejará programas simples con un estado de programa bastante razonable. Por ejemplo, se llama a cualquier bloque final. Si quisieras ser un verdadero imbécil, podrías atrapar ThreadDeath (o Error), ¡y seguir corriendo!
Si nada más, esto realmente me hace desear más código seguido el enfoque IProgressMonitor - agregar otro parámetro a los métodos que pueden llevar un tiempo, y alentar al implementador del método a sondear ocasionalmente el objeto Monitor para ver si el usuario quiere que el sistema dé arriba. Trataré de seguir este patrón en el futuro, especialmente los métodos que podrían ser interactivos. Por supuesto, no necesariamente se sabe de antemano qué métodos se utilizarán de esta manera, pero para eso están los Profilers, supongo.
En cuanto al método "iniciar otra JVM por completo", eso requerirá más trabajo. No sé si alguien ha escrito un cargador de clase delegante, o si uno está incluido en la JVM, pero eso sería necesario para este enfoque.
La respuesta correcta es, creo, crear un Runnable para ejecutar el subprograma y ejecutarlo en un subproceso separado. El Runnable puede ser un FutureTask, que puede ejecutar con un tiempo de espera (método "get"). Si se agota el tiempo, obtendrás una TimeoutException, en la que te sugiero
- invoca thread.interrupt () para intentar finalizarlo de manera semi-cooperativa (muchas llamadas a la biblioteca parecen ser sensibles a esto, por lo que probablemente funcione)
- espera un poco (Thread.sleep (300))
- y luego, si el hilo todavía está activo (thread.isActive ()), llame a thread.stop (). Este es un método obsoleto, pero aparentemente es el único juego en la ciudad excepto ejecutar un proceso separado con todo lo que esto conlleva.
En mi aplicación, donde ejecuto un código no cooperativo y no cooperativo escrito por mis estudiantes principiantes, hago lo anterior, asegurándome de que el hilo asesinado nunca tenga acceso (de escritura) a ningún objeto que sobreviva a su muerte. Esto incluye el objeto que alberga el método llamado, que se descarta si se produce un tiempo de espera. (Les digo a mis alumnos que eviten los tiempos muertos, porque su agente será descalificado). No estoy seguro de si hay pérdidas de memoria ...
Distinguir entre los tiempos de ejecución largos (el método termina) y los tiempos muertos difíciles: los tiempos muertos más duros son más largos y pretenden atrapar el caso cuando el código no finaliza en absoluto, en lugar de ser lento.
Según mi investigación, Java no parece tener una disposición no obsoleta para ejecutar código no cooperativo, que, de alguna manera, es un gran vacío en el modelo de seguridad. O bien puedo ejecutar código extranjero y controlar los permisos que tiene (SecurityManager), o no puedo ejecutar código extraño, porque podría acabar ocupando toda una CPU sin medios no desaprovechados para detenerlo.
double x = 2.0;
while(true) {x = x*x}; // do not terminate
System.out.print(x); // prevent optimization
Nadie respondió directamente, así que aquí está lo más cercano que puedo darte en una pequeña cantidad de código psuedo:
envuelva el método en un ejecutable / invocable. El método en sí tendrá que verificar si hay un estado interrumpido si desea que se detenga (por ejemplo, si este método es un ciclo, dentro de la comprobación de bucle para Thread.currentThread (). IsInterrupted y, si es así, detenga el ciclo (don Sin embargo, compruebe cada iteración, o simplemente disminuya la velocidad en el método de envoltura, use thread.join (timeout) para esperar el momento en que quiera que se ejecute el método o, dentro de un ciclo, llame al join repetidamente con un tiempo de espera menor si necesita hacer otras cosas mientras espera. Si el método no termina, después de unirse, use las recomendaciones anteriores para abortar rápido / limpio.
tan sabio código, viejo código:
void myMethod()
{
methodTakingAllTheTime();
}
nuevo código:
void myMethod()
{
Thread t = new Thread(new Runnable()
{
public void run()
{
methodTakingAllTheTime(); // modify the internals of this method to check for interruption
}
});
t.join(5000); // 5 seconds
t.interrupt();
}
pero nuevamente, para que esto funcione bien, igual tendrá que modificar el métodoTakingAllTheTime o ese hilo continuará ejecutándose después de que haya llamado a interrupt.
Puedo pensar en una forma no tan buena de hacer esto. Si puede detectar cuándo tarda demasiado tiempo, puede hacer que el método compruebe si hay un valor booleano en cada paso. Haga que el programa cambie el valor de boolean tooMuchTime a true si le toma demasiado tiempo (no puedo ayudar con esto). Luego usa algo como esto:
Method(){
//task1
if (tooMuchTime == true) return;
//task2
if (tooMuchTime == true) return;
//task3
if (tooMuchTime == true) return;
//task4
if (tooMuchTime == true) return;
//task5
if (tooMuchTime == true) return;
//final task
}