Why does Task.Delay() allow an infinite delay?

2019-02-12 08:50发布

问题:

After my application froze I tracked down the cause to a thread waiting on a task created by Task.Delay() (or TaskEx.Delay() in .NET 4.0) for which it provided a computed TimeSpan that, due to a bug, was on occasion computed to a TimeSpan with a TotalMilliseconds of less than or equal to -1 and greater than -2 (i.e. anywhere between -10000 to -19999 ticks, inclusive).

It appears that when you pass a negative TimeSpan that is -2 milliseconds or lower, the method correctly throws an ArgumentOutOfRangeException, but when you provide a negative TimeSpan from the range described above, it returns a Task that never completes (by setting the underlying System.Threading.Timer to a dueTime of -1 which denotes infinity). That means that any continuations set on that task will never execute, and any poor thread that happens to .Wait() on that Task will forever be blocked.

What possible use can a Task that never completes have? Would anyone expect such a return value? Shouldn't any negative value passed to .Delay(), including values in that special range, throw an ArgumentOutOfRangeException?

回答1:

Timeout.Infinite or -1 is useful when you want to wait indefinitely for a long-running task that will take an indeterminate amount of time to complete, but will eventually complete.

The Win32 API also uses a constant INFINITE = -1 for infinite timeouts.

You wouldn't normally want to use it in a UI thread, as it could freeze the UI (which seems to be your problem). But there are valid use cases in a worker thread - e.g. a server that is blocking waiting for a connection from a client.



回答2:

In mocking scenarios, where I want to ensure that my code in a Task.WhenAny() block is handling one of the tasks awaited on correctly, I may mock the other tasks and use an infinite delay to ensure that the Task.WhenAny is processing the task I did not mock as an infinite delay.



回答3:

I'm using it when I want console application not to terminate. For example, when I'm writing a Web Job for Auzre, I need a program that never terminates. Here's the template:

public async Task Main(string args[])
{
    var timer = new Timer();
    timer.Interval = 6000;
    timer.Elapsed += (sender, args) =>
    {
        // Do something repeatedly, each 1 minute.
    };
    timer.Start();

    // Now what to do to stop the application from terminating!?
    // That's the magic:
    await Task.Delay(-1);
}