Quiero esperar a que se complete una Tarea <T> con algunas reglas especiales: si no se ha completado después de X milisegundos, quiero mostrar un mensaje al usuario. Y si no se ha completado después de Y milisegundos, quiero solicitar automáticamente la cancelación .
Puedo usar Task.ContinueWith para esperar asincrónicamente a que se complete la tarea (es decir, programar una acción para que se ejecute cuando se complete la tarea), pero eso no permite especificar un tiempo de espera. Puedo usar Task.Wait para esperar sincrónicamente a que la tarea se complete con un tiempo de espera, pero eso bloquea mi hilo. ¿Cómo puedo esperar asincrónicamente a que la tarea se complete con un tiempo de espera?
CancellationTokenSource
. Hay dos sobrecargas para el constructor, una con un retraso de milisegundos entero y otra con un retraso de TimeSpan.Respuestas:
Qué tal esto:
Y aquí hay una gran publicación de blog "Elaborando una tarea. Método Timeout After After" (del equipo de la Biblioteca paralela de MS) con más información sobre este tipo de cosas .
Además : a solicitud de un comentario sobre mi respuesta, aquí hay una solución ampliada que incluye el manejo de la cancelación. Tenga en cuenta que pasar la cancelación a la tarea y al temporizador significa que hay varias formas en que se puede experimentar la cancelación en su código, y debe asegurarse de probar y estar seguro de manejarlos adecuadamente. No deje al azar varias combinaciones y espere que su computadora haga lo correcto en tiempo de ejecución.
fuente
Task.Delay
tarea está respaldada por un temporizador del sistema que continuará siendo rastreado hasta que expire el tiempo de espera, independientemente de cuánto tiempoSomeOperationAsync
tarde. Entonces, si este fragmento de código general se ejecuta mucho en un ciclo cerrado, está consumiendo recursos del sistema para temporizadores hasta que se agote el tiempo de espera. La forma de solucionarlo sería tener unCancellationToken
paseTask.Delay(timeout, cancellationToken)
que cancele cuando seSomeOperationAsync
complete para liberar el recurso del temporizador.Task
, cualquier excepción almacenada por la tarea se vuelve a lanzar en ese punto. Esto le da la oportunidad de atraparOperationCanceledException
(si se cancela) o cualquier otra excepción (si falla).Task.Wait(timeout)
bloquearía sincrónicamente en lugar de esperar asincrónicamente.Aquí hay una versión del método de extensión que incorpora la cancelación del tiempo de espera cuando la tarea original se completa como lo sugiere Andrew Arnott en un comentario a su respuesta .
fuente
using
bloquetask.Result
cuando se ejecuta dos veces.task
ejecutándose la tarea original ( ) en caso de tiempo de espera?TimeoutException
tiene un mensaje predeterminado adecuado. Al anularlo con "La operación ha excedido el tiempo de espera". no agrega valor y en realidad causa cierta confusión al implicar que hay una razón para anularlo.Puede usar
Task.WaitAny
para esperar la primera de varias tareas.Puede crear dos tareas adicionales (que se completan después de los tiempos de espera especificados) y luego usar
WaitAny
para esperar lo que se complete primero. Si la tarea que se completó primero es su tarea de "trabajo", entonces ya está. Si la tarea que se completó primero es una tarea de tiempo de espera, entonces puede reaccionar al tiempo de espera (por ejemplo, solicitud de cancelación).fuente
below
... ¿cuál es esa? basado en pedidos SO?¿Qué tal algo como esto?
Puede usar la opción Task.Wait sin bloquear el hilo principal usando otra tarea.
fuente
Aquí hay un ejemplo completamente trabajado basado en la respuesta más votada, que es:
La principal ventaja de la implementación en esta respuesta es que se han agregado genéricos, por lo que la función (o tarea) puede devolver un valor. Esto significa que cualquier función existente se puede incluir en una función de tiempo de espera, por ejemplo:
Antes de:
Después:
Este código requiere .NET 4.5.
Advertencias
Habiendo dado esta respuesta, generalmente no es una buena práctica tener excepciones en su código durante la operación normal, a menos que tenga que:
Solo use este código si no puede alterar absolutamente la función que está llamando, por lo que se agota después de un tiempo específico
TimeSpan
.Esta respuesta solo es aplicable cuando se trata de bibliotecas de bibliotecas de terceros que simplemente no puede refactorizar para incluir un parámetro de tiempo de espera.
Cómo escribir código robusto
Si desea escribir código robusto, la regla general es esta:
Si no observa esta regla, su código eventualmente golpeará una operación que falla por alguna razón, luego se bloqueará indefinidamente y su aplicación se bloqueará permanentemente.
Si hubo un tiempo de espera razonable después de un tiempo, entonces su aplicación se colgaría durante un período de tiempo extremo (por ejemplo, 30 segundos) y luego mostraría un error y continuaría su camino feliz, o volvería a intentarlo.
fuente
Usando la excelente biblioteca AsyncEx de Stephen Cleary , puede hacer:
TaskCanceledException
será lanzado en caso de un tiempo de espera.fuente
Esta es una versión ligeramente mejorada de respuestas anteriores.
CancellationToken
la tarea original, y cuando ocurre el tiempo de espera, obtiene enTimeoutException
lugar deOperationCanceledException
.Uso
InnerCallAsync
puede tardar mucho tiempo en completarse.CallAsync
lo envuelve con un tiempo de espera.fuente
timeoutCancellation
endelayTask
. Actualmente, si cancela la cancelación,CancelAfterAsync
puede lanzar enTimeoutException
lugar deTaskCanceledException
, la causadelayTask
puede terminar primero.WhenAny
sean canceladas por el mismo token,WhenAny
devolverá la primera tarea. Esa suposición estaba mal. He editado la respuesta. ¡Gracias!Use un temporizador para manejar el mensaje y la cancelación automática. Cuando finalice la tarea, llame a Dispose en los temporizadores para que nunca se disparen. Aquí hay un ejemplo; cambie taskDelay a 500, 1500 o 2500 para ver los diferentes casos:
Además, el CTP asíncrono proporciona un método TaskEx.Delay que envolverá los temporizadores en tareas para usted. Esto puede darle más control para hacer cosas como configurar el TaskScheduler para la continuación cuando se dispara el temporizador.
fuente
task.Wait()
.Otra forma de resolver este problema es usar extensiones reactivas:
Prueba arriba usando el siguiente código en la prueba de tu unidad, funciona para mí
Es posible que necesite el siguiente espacio de nombres:
fuente
Una versión genérica de la respuesta de @ Kevan anterior, usando Extensiones reactivas.
Con programador opcional:
Por cierto: cuando ocurre un tiempo de espera, se generará una excepción de tiempo de espera
fuente
Si usa una BlockingCollection para programar la tarea, el productor puede ejecutar la tarea potencialmente larga y el consumidor puede usar el método TryTake que tiene incorporado el token de tiempo de espera y cancelación.
fuente
Sentí la
Task.Delay()
tarea yCancellationTokenSource
en las otras respuestas un poco demasiado para mi caso de uso en un circuito de red cerrado.Y aunque Joe Hoag's Crafting a Task.TimeoutAfter Method en los blogs de MSDN fue inspirador, estaba un poco cansado de usar
TimeoutException
el control de flujo por la misma razón que antes, porque los tiempos de espera se esperan con mayor frecuencia que no.Así que seguí con esto, que también maneja las optimizaciones mencionadas en el blog:
Un ejemplo de caso de uso es como tal:
fuente
Algunas variantes de la respuesta de Andrew Arnott:
Si desea esperar una tarea existente y averiguar si se completó o se agotó el tiempo de espera, pero no desea cancelarla si ocurre el tiempo de espera:
Si desea iniciar una tarea de trabajo y cancelar el trabajo si se produce el tiempo de espera:
Si ya ha creado una tarea que desea cancelar si se produce un tiempo de espera:
Otro comentario, estas versiones cancelarán el temporizador si no se produce el tiempo de espera, por lo que varias llamadas no harán que los temporizadores se acumulen.
sjb
fuente
Estoy recombinando las ideas de algunas otras respuestas aquí y esta respuesta en otro hilo en un método de extensión al estilo Try. Esto tiene un beneficio si desea un método de extensión, pero evita una excepción en el tiempo de espera.
fuente
Definitivamente no hagas esto, pero es una opción si ... No puedo pensar en una razón válida.
fuente