10

I need a task that never ends until cancellation is requested. At the moment the simplest way to do that is:

var cancellation = new CancellationTokenSource();

var task = Task.Factory.StartNew(async () =>
{
    while (true)
    {
        await Task.Delay(10000, cancellation.Token);
    }
}, cancellation.Token).Unwrap();

What I don't like is a call to Task.Delay method because it requires limited time interval for waiting.

Is there more elegant solution?

Community
  • 1
  • 1
neleus
  • 2,230
  • 21
  • 36
  • Does the task need to count as being started? Also, even if you ignore the possibility of an infinite timeout, you can still give `TimeSpan.MaxValue` which simply won't finish... – Jon Skeet May 03 '14 at 15:53
  • Yes, the task is started. Regarding `TimeSpan.MaxValue` Im not sure if it will work, I haven't found any information in docs. – neleus May 06 '14 at 12:37
  • That task *is* started - but do you *need* it to be started? – Jon Skeet May 06 '14 at 13:18
  • In 99% cases yes, I need the task to be started. Why do you ask, is it significant? – neleus May 06 '14 at 14:07
  • I was thinking of `TaskCompletionSource`, as Lee suggested - but a `TaskCompletionSource`-based task which hasn't been finished is in state `WaitingForActivation`, which may not be appropriate for you. – Jon Skeet May 06 '14 at 14:32

2 Answers2

11

As an alternative to TaskCompletionSource with token.Register, here are some one-liners:

var task = new Task(() => {}, token); // don't do task.Run()!

Or, simply this:

var task = Task.Delay(Timeout.Infinite, token);

There's even a nice optimization for Timeout.Infinite in the current Task.Delay implementation.

noseratio
  • 59,932
  • 34
  • 208
  • 486
  • 3
    Last case I think is the simplest so the most elegant, looks great. I forgot about this "magic" constant `Timeout.Infinite`, luckily this method accepts it. – neleus May 06 '14 at 12:50
9

You should be able to subscribe to the cancellation of the token and complete the task then:

public static Task UntilCancelled(CancellationToken tok)
{
    var tcs = new TaskCompletionSource<object>();
    IDisposable subscription = null;
    subscription = tok.Register(() =>
    {
        tcs.SetResult(null);
        subscription.Dispose();
    });

    return tcs.Task;
}
Lee
  • 142,018
  • 20
  • 234
  • 287
  • This is nice. And you can really slim it down with this: `using var subscription = tok.Register(() => tcs.SetResult(null)); await tcs.Task;` – Justin J Stark Jul 15 '19 at 21:34