将超时添加到复杂的Task.WhenAll

时间:2019-04-19 13:58:29

标签: c#

我有一个运行异步任务(已注释的任务)的任务收集器:

public Task<ResultOrException<T>[]> WhenAllOrException<T>(IEnumerable<Task<T>> tasks)
{
    return Task.WhenAll(
        tasks.Select(
            task => task.ContinueWith(
                t => t.IsFaulted
                    ? new ResultOrException<T>(t.Exception)
                    : new ResultOrException<T>(t.Result))));       
}

public class ResultOrException<T>
{
    public ResultOrException(T result)
    {
        IsSuccess = true;
        Result = result;
    }

    public ResultOrException(Exception ex)
    {
        IsSuccess = false;
        Exception = ex;
    }

    public bool IsSuccess { get; }
    public T Result { get; }
    public Exception Exception { get; }
} 

此代码等待所有任务,即使它们在2分钟后返回。 我需要忽略所有任务返回,超时为5秒。

我也尝试过以此方式更改代码,但无法编译:

return Task.WhenAny(Task.WhenAll(
    tasks.Select(
            task => task.ContinueWith(
                t => t.IsFaulted
                    ? new ResultOrException<T>(t.Exception)
                    : new ResultOrException<T>(t.Result)))

    ), Task.Delay(2000));

或者这个:

var ts = new TimeSpan(1000);

        return await Task.WaitAll(
            tasks.Select(
                task => task.ContinueWith(
                    t => t.IsFaulted
                        ? new ResultOrException<T>(t.Exception)
                        : new ResultOrException<T>(t.Result))),
            ts);

如何实现?

1 个答案:

答案 0 :(得分:1)

  

它不会编译

这是因为Task.Delay返回了Task。那里没有价值或例外。因此,它不能与ResultOrException<T>序列直接组合。

您需要确定如何向呼叫者报告超时。如果您希望Task<ResultOrException<T>[]>出故障,则可以执行以下操作:

public async Task<ResultOrException<T>[]> WhenAllOrException<T>(IEnumerable<Task<T>> tasks)
{
    var resultOrExceptions = Task.WhenAll(
        tasks.Select(task => ...)
    );
    var delayTask = Task.Delay(2000);
    var completedTask = await Task.WhenAny(resultOrExceptions, delayTask);
    if (completedTask == delayTask)
        throw new TimeoutException();
    return await resultOrExceptions;
}

或者,如果您要返回一个数组ResultOrException<T>,每个数组都有一个超时错误,那么您可以这样做:

public async Task<ResultOrException<T>[]> WhenAllOrException<T>(IEnumerable<Task<T>> tasks)
{
    var resultOrExceptionTasks = tasks.Select(task => ...)
        .ToArray();
    var resultOrExceptions = Task.WhenAll(resultOrExceptionTasks);
    var delayTask = Task.Delay(2000);
    var completedTask = await Task.WhenAny(resultOrExceptions, delayTask);
    if (completedTask == delayTask)
        return Enumerable.Repeat(new ResultOrException<T>(new TimeoutException()), resultOrExceptionTasks.Length).ToArray();
    return await resultOrExceptions;
}

或者,如果您想返回及时生成的结果,而只返回未超时的异常,那么您想将WhenAny 放入内部WhenAll

public Task<ResultOrException<T>[]> WhenAllOrException<T>(IEnumerable<Task<T>> tasks)
{
    var delayTask = Task.Delay(2000);
    return Task.WhenAll(tasks.Select(WithTimeout));

    async Task<ResultOrException<T>> WithTimeout(Task<T> task)
    {
        var completedTask = await Task.WhenAny(task, delayTask);
        if (completedTask == delayTask)
            return new ResultOrException<T>(new TimeoutException());
        try
        {
            return new ResultOrException<T>(await task);
        }
        catch (Exception ex)
        {
            return new ResultOrException<T>(ex);
        }
    }
}

旁注:您应该always pass a TaskScheduler to ContinueWith。另外,我有一个Try implementation可能会有用。