Espere asincrónicamente a que la Tarea <T> se complete con el tiempo de espera

Resuelto dtb asked hace 13 años • 19 respuestas

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 la cancelación automáticamente .

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 se complete la tarea con un tiempo de espera, pero eso bloquea mi hilo. ¿Cómo puedo esperar de forma asincrónica a que se complete la tarea con un tiempo de espera?

dtb avatar Nov 21 '10 21:11 dtb
Aceptado

Qué tal esto:

int timeout = 1000;
var task = SomeOperationAsync();
if (await Task.WhenAny(task, Task.Delay(timeout)) == task) {
    // task completed within timeout
} else { 
    // timeout logic
}

Y aquí hay una excelente publicación de blog "Creación de un método Task.TimeoutAfter" (del equipo de MS Parallel Library) 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 cancelaciones. 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 probarlas y estar seguro de que las maneja todas correctamente. No dejes al azar varias combinaciones y espera que tu computadora haga lo correcto en tiempo de ejecución.

int timeout = 1000;
var task = SomeOperationAsync(cancellationToken);
if (await Task.WhenAny(task, Task.Delay(timeout, cancellationToken)) == task)
{
    // Task completed within timeout.
    // Consider that the task may have faulted or been canceled.
    // We re-await the task so that any exceptions/cancellation is rethrown.
    await task;

}
else
{
    // timeout/cancellation logic
}
Andrew Arnott avatar Jun 25 '2012 14:06 Andrew Arnott

Aquí hay una versión del método de extensión que incorpora la cancelación del tiempo de espera cuando se completa la tarea original como lo sugiere Andrew Arnott en un comentario a su respuesta .

public static async Task<TResult> TimeoutAfter<TResult>(this Task<TResult> task, TimeSpan timeout) {

    using (var timeoutCancellationTokenSource = new CancellationTokenSource()) {

        var completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token));
        if (completedTask == task) {
            timeoutCancellationTokenSource.Cancel();
            return await task;  // Very important in order to propagate exceptions
        } else {
            throw new TimeoutException("The operation has timed out.");
        }
    }
}
Redwood avatar Feb 27 '2014 19:02 Redwood