What is the best way to complete a task to prevent escape

I've created a function below that will wait for all tasks to complete or throw an exception when canceled or timed out.

public static async Task WhenAll(
    IEnumerable<Task> tasks, 
    CancellationToken cancellationToken,
    int millisecondsTimeOut)
{
    Task timeoutTask = Task.Delay(millisecondsTimeOut, cancellationToken);
    Task completedTask = await Task.WhenAny(
        Task.WhenAll(tasks), 
        timeoutTask
    );
    if (completedTask == timeoutTask)
    {
        throw new TimeoutException();
    }
}

      

If everything is tasks

finished before a long timeout (i.e. millisecondsTimeOut

= 60,000), will there timeoutTask

remain about 60 seconds even after the function returns? If so, what is the best way to fix the flight problem?

+3


source to share


1 answer


Yes, it timeoutTask

will hang until this timeout expires (or canceled CancellationToken

).

You can fix this by passing another CancellationToken

one you get from the new CancellationTokenSource

one you create using CancellationTokenSource.CreateLinkedTokenSource

and canceling at the end. You must also wait for the task to complete, otherwise you won't see an exception (or cancellation):

public static async Task WhenAll(
    IEnumerable<Task> tasks,
    CancellationToken cancellationToken,
    int millisecondsTimeOut)
{
    var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
    var timeoutTask = Task.Delay(millisecondsTimeOut, cancellationTokenSource.Token);
    var completedTask = await Task.WhenAny(Task.WhenAll(tasks), timeoutTask);
    if (completedTask == timeoutTask)
    {
        throw new TimeoutException();
    }

    cancellationTokenSource.Cancel();
    await completedTask;
}

      



However, I think there is an easier way to achieve what you want, if you don't need to differentiate between TimeoutException

and TaskCancelledException

. You just add a continuation that is canceled when CancellationToken

canceled or when the timeout is complete:

public static Task WhenAll(
    IEnumerable<Task> tasks,
    CancellationToken cancellationToken,
    int millisecondsTimeOut)
{
    var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
    cancellationTokenSource.CancelAfter(millisecondsTimeOut);

    return Task.WhenAll(tasks).ContinueWith(
        _ => _.GetAwaiter().GetResult(), 
        cancellationTokenSource.Token,
        TaskContinuationOptions.ExecuteSynchronously, 
        TaskScheduler.Default);
}

      

+2


source







All Articles