为什么Task.WhenAny没有抛出预期的TimeoutException?

时间:2015-07-21 16:30:21

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

请注意以下简单的代码:

class Program
{
    static void Main()
    {
        var sw = new Stopwatch();
        sw.Start();
        try
        {
            Task.WhenAny(RunAsync()).GetAwaiter().GetResult();
        }
        catch (TimeoutException)
        {
            Console.WriteLine("Timed out");
        }
        Console.WriteLine("Elapsed: " + sw.Elapsed);
        Console.WriteLine("Press Enter to exit");
        Console.ReadLine();
    }

    private static async Task RunAsync()
    {
        await Observable.StartAsync(async ct =>
        {
            for (int i = 0; i < 10; ++i)
            {
                await Task.Delay(500, ct);
                Console.WriteLine("Inside " + i);
            }
            return Unit.Default;
        }).Timeout(TimeSpan.FromMilliseconds(1000));
    }
}

运行输出:

Inside 0
Inside 1
Elapsed: 00:00:01.1723818
Press Enter to exit

注意,没有超时消息。

现在,如果我将Task.WhenAny替换为Task.WhenAll,这就是我所得到的:

Inside 0
Inside 1
Timed out
Elapsed: 00:00:01.1362188
Press Enter to exit

请注意此次出现超时消息。

并且,如果我完全删除Task.WhenAll包装并直接致电RunAsync

Inside 0
Inside 1
Timed out
Elapsed: 00:00:01.1267617
Press Enter to exit

正如预期的那样,超时消息就在那里。

那么Task.WhenAny的交易是什么?它显然会中断异步方法,但TimeoutException

在哪里

1 个答案:

答案 0 :(得分:24)

Task.WhenAny不会从单个任务中重新抛出异常(与Task.WhenAll不同):

  

“当任何提供的任务完成时,返回的任务将完成。返回的任务将始终以RanToCompletion状态结束,并将Result设置为第一个任务完成。这是真的即使完成的第一项任务在CanceledFaulted状态结束。“

来自Task.WhenAny

这意味着无论没有任何类型的例外,它都会成功完成。

要实际重新抛出单个已完成任务的例外,您需要await返回的任务本身:

var completedTask = await Task.WhenAny(tasks); // no exception
await completedTask; // possible exception

或者在你的情况下:

Task.WhenAny(RunAsync()).GetAwaiter().GetResult().GetAwaiter().GetResult();