2009-09-11 25 views

Respuesta

12

Ok, aquí está la verdadera respuesta.

... 

void LongRunningMethod(object monitorSync) 
{ 
    //do stuff  
    lock (monitorSync) { 
    Monitor.Pulse(monitorSync); 
    } 
} 

void ImpatientMethod() { 
    Action<object> longMethod = LongRunningMethod; 
    object monitorSync = new object(); 
    bool timedOut; 
    lock (monitorSync) { 
    longMethod.BeginInvoke(monitorSync, null, null); 
    timedOut = !Monitor.Wait(monitorSync, TimeSpan.FromSeconds(30)); // waiting 30 secs 
    } 
    if (timedOut) { 
    // it timed out. 
    } 
} 

    ... 

Esto combina dos de las partes más divertidas de usar C#. En primer lugar, para llamar al método de forma asincrónica, utilice un delegado que tenga la magia BeginInvoke de pantalones de fantasía.

A continuación, utilice un monitor para enviar un mensaje desde LongRunningMethod al ImpatientMethod para avisarle cuando lo haya hecho, o si no ha tenido noticias suyas en un cierto período de tiempo, simplemente renuncie a él.

(ps Es broma de que esta es la verdadera respuesta. Yo sé que hay 2^9303 maneras de pelar un gato. Especialmente en .Net)

+0

Esto funcionó para mí :) –

+4

Funciona, pero! ¡Ten cuidado!- cuando vuelve el flujo de ejecución, el "LongRunningMethod" todavía se ejecuta en segundo plano. Esto puede ser una fuga. – Alex

+0

Eso es totalmente cierto. Esto solo es bueno si quiere dejar de esperar una acción, no detener una acción. – MojoFilter

7

No puede hacer eso, a menos que cambie el método.

Hay dos maneras:

  1. El método se construye de tal manera que ella misma mide el tiempo que ha estado funcionando, y luego vuelve prematuramente si supera cierto umbral.
  2. El método está construido de tal manera que monitorea una variable/evento que dice "cuando esta variable está configurada, por favor salga", y luego tiene otro subproceso mide el tiempo pasado en el primer método, y luego establece que variable cuando el tiempo transcurrido ha excedido algún umbral.

La respuesta más obvia, pero desafortunadamente, que puede obtener aquí es "Simplemente ejecute el método en un hilo y use Thread.Abort cuando se ha ejecutado durante demasiado tiempo".

La única forma correcta es que el método coopere de forma que salga limpio cuando ha estado funcionando demasiado tiempo.

También hay una tercera forma, donde ejecuta el método en un subproceso separado, pero después de esperar a que termine, y tarda demasiado en hacerlo, simplemente dice "No voy a esperar a que terminar, pero solo descartarlo ". En este caso, el método seguirá ejecutándose, y eventualmente terminará, pero ese otro hilo que estaba esperando simplemente se dará por vencido.

Piense en la tercera manera de llamar a alguien y pedirle que busque en su casa el libro que le prestó, y después de esperar en el teléfono durante 5 minutos, simplemente diga "aw, tira", y colgar. Eventualmente esa otra persona encontrará el libro y volverá al teléfono, solo para darse cuenta de que ya no se preocupa por el resultado.

0

Los métodos no tienen tiempos de espera en C#, a menos que esté en el depurador o que el sistema operativo crea que su aplicación se ha 'colgado'. Incluso entonces, el procesamiento continúa y, siempre que no mate la aplicación, se devuelve una respuesta y la aplicación continúa funcionando.

Las llamadas a bases de datos pueden tener tiempos de espera.

2

Puede ejecutar el método en una secuencia separada, y supervisarlo y forzarlo a salir si funciona demasiado tiempo. Una buena forma, si puede llamarlo así, sería desarrollar un atributo para el método en Post Sharp para que el código de observación no ensucie su aplicación.

He escrito el siguiente código de ejemplo como (tenga en cuenta la parte de código de ejemplo, funciona, pero podría sufrir problemas de múltiples hilos, o si el método en cuestión captura la ThreadAbortException lo rompería):

static void ActualMethodWrapper(Action method, Action callBackMethod) 
{ 
    try 
    { 
     method.Invoke(); 
    } catch (ThreadAbortException) 
    { 
     Console.WriteLine("Method aborted early"); 
    } finally 
    { 
     callBackMethod.Invoke(); 
    } 
} 

static void CallTimedOutMethod(Action method, Action callBackMethod, int milliseconds) 
{ 
    new Thread(new ThreadStart(() => 
    { 
     Thread actionThread = new Thread(new ThreadStart(() => 
     { 
      ActualMethodWrapper(method, callBackMethod); 
     })); 

     actionThread.Start(); 
     Thread.Sleep(milliseconds); 
     if (actionThread.IsAlive) actionThread.Abort(); 
    })).Start(); 
} 

con la siguiente invocación:

CallTimedOutMethod(() => 
{ 
    Console.WriteLine("In method"); 
    Thread.Sleep(2000); 
    Console.WriteLine("Method done"); 
},() => 
{ 
    Console.WriteLine("In CallBackMethod"); 
}, 1000); 

tengo que trabajar en mi legibilidad del código.

+0

Todavía es necesario escribir código en el método para hacer una salida limpia cuando debería terminar. –

+0

¿Quiere decir algo como capturar la excepción ThreadAbort y limpiar recursos/proporcionar código de salida? Realmente dependería del método en cuestión. –

+0

Si con "timeout" quiere decir "Quiero cerrar mi programa y no espero a que se complete el método", entonces sí, Thread.Abort se puede usar, pero ese es realmente el único escenario. –

0

¿Podría crear un Asynchronous Method para que pueda continuar haciendo otras cosas mientras se completa el método "ocupado"?

3

Mientras MojoFilter's answer es agradable que puede dar lugar a fugas si el " LongMethod "se congela. Debes ABORTAR la operación si ya no estás interesado en el resultado.

public void LongMethod() 
{ 
    //do stuff 
} 

public void ImpatientMethod() 
{ 
    Action longMethod = LongMethod; //use Func if you need a return value 

    ManualResetEvent mre = new ManualResetEvent(false); 

    Thread actionThread = new Thread(new ThreadStart(() => 
    { 
     var iar = longMethod.BeginInvoke(null, null); 
     longMethod.EndInvoke(iar); //always call endinvoke 
     mre.Set(); 
    })); 

    actionThread.Start(); 
    mre.WaitOne(30000); // waiting 30 secs (or less) 
    if (actionThread.IsAlive) actionThread.Abort(); 
} 
0

Escribo regularmente aplicaciones en las que tengo que sincronizar tareas de tiempo crítico entre plataformas. Si puedes evitar thread.abort debes hacerlo. Consulte http://blogs.msdn.com/b/ericlippert/archive/2010/02/22/should-i-specify-a-timeout.aspx y http://www.interact-sw.co.uk/iangblog/2004/11/12/cancellation para obtener instrucciones sobre cuándo thread.abort es apropiado. Éstos son el concepto implemento:

  • ejecución selectiva: Sólo se ejecuta si una posibilidad razonable de éxito existe (basado en la capacidad de cumplir con el tiempo de espera o la probabilidad de resultado el éxito en relación con otros elementos en cola). Si divide el código en segmentos y conoce aproximadamente el tiempo esperado entre los fragmentos de tareas, puede predecir si debe omitir cualquier procesamiento posterior. Se puede medir el tiempo total envolviendo las tareas de un contenedor de objetos con una función recursiva para el cálculo del tiempo o teniendo una clase de controlador que observa a los trabajadores conocer los tiempos de espera esperados.
  • Orfandad selectiva: solo espere la devolución si existe una posibilidad razonable de éxito. Las tareas indexadas se ejecutan en una cola administrada. Las tareas que exceden su tiempo de espera o riesgo de causar otros tiempos de espera quedan huérfanas y se devuelve un registro nulo en su lugar. Las tareas que se ejecutan más largas se pueden envolver en llamadas asíncronas. Consulte el ejemplo de contenedor de llamadas asíncronas: http://www.vbusers.com/codecsharp/codeget.asp?ThreadID=67&PostID=1
  • Selección condicional: similar a la ejecución selectiva pero basada en grupos en lugar de tareas individuales. Si muchas de sus tareas están interconectadas de forma tal que un éxito o un fracaso hacen que el procesamiento adicional sea irrelevante, cree una marca que esté marcada antes de que comience la ejecución y nuevamente antes de que comiencen las subtareas de ejecución larga. Esto es especialmente útil cuando usa parallel.for u otras tareas de concurrencia en cola.
3

Esta es una vieja pregunta pero ahora tiene una solución más simple que no estaba disponible: ¡Tareas!

Aquí es un ejemplo de código:

var task = Task.Run(() => LongRunningMethod());//you can pass parameters to the method as well 
if (task.Wait(TimeSpan.FromSeconds(30))) 
    return task.Result; //the method returns elegantly 
else 
    throw new TimeoutException();//the method timed-out 
Cuestiones relacionadas