【问题标题】:Why does the Task.WhenAny not throw an expected TimeoutException?为什么 Task.WhenAny 不会抛出预期的 TimeoutException?
【发布时间】:2015-10-11 05:44:56
【问题描述】:

请注意以下琐碎的代码:

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

注意这次 Timed out 消息的存在。

而且,如果我完全删除 Task.WhenAll 包装器并直接调用 RunAsync

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

Timed out 消息如预期的那样存在。

那么Task.WhenAny 是怎么回事?明明打断了异步方法,但是TimeoutException在哪里呢?

【问题讨论】:

  • 你为什么用.Task.GetAwaiter().GetResult(),首先它等同于Task.Result,其次你很可能会被一个grue吃掉。
  • @Aron 这不是等价的。它抛出了实际的异常。但是,是的,它仍然处于阻塞状态,可能会导致死锁。
  • Task.getAwaiter(): This method is intended for compiler use rather than for use in application code.
  • @sstan - 我知道,但它是唯一真正等同于await 的阻塞。 .Result.Wait() 具有不同的异常处理语义。

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


【解决方案1】:

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

【讨论】:

  • 天哪,它返回一个Task&lt;Task&gt;。现在说得通了,但是为什么会有这么奇怪的返回类型呢?
  • @mark 外部任务在那里,所以你可以等待它。内部任务是让您获得第一个完成的任务作为结果。当您使用Task.WhenAny 时,您可能想知道哪个任务先完成,或者它的结果是什么。
  • 这是否意味着你可以合法地写await await Task.WhenAny(tasks)@i3arnon?!
  • 请注意,虽然您可以合法地编写 await await Task.WhenAny(tasks),但只有第一个任务的结果/状态会以这种方式检查。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-09-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-06
  • 2013-02-18
相关资源
最近更新 更多