结束任务以防止逃跑的最佳方法是什么

时间:2015-08-07 19:45:53

标签: c# .net async-await task-parallel-library task

我创建了下面的函数,它将等待所有任务完成或在发生取消或超时时引发异常。

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();
    }
}

如果所有tasks在很长时间之前完成(即millisecondsTimeOut = 60,000),那么即使在函数返回后,timeoutTask会一直待到60秒吗?如果是,解决失控问题的最佳方法是什么?

1 个答案:

答案 0 :(得分:2)

是的,timeoutTask会暂停,直到超时结束(或CancellationToken被取消)。

您可以通过传递使用CancellationToken创建的新CancellationTokenSource获得的其他CancellationTokenSource.CreateLinkedTokenSource并在结束时取消来修复此问题。您还应该等待完成的任务,否则您不会真正观察异常(或取消):

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;
}

但是,如果您不需要区分TimeoutExceptionTaskCancelledException,我认为这是实现您想要的更简单的方法。您只需添加在取消CancellationToken或超时结束时取消的延续:

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);
}