6

Is it possible to use System.Threading.Task.Task to create a loop of task that can be cancelled?

The flow should start with a Task.Delay(x ms) then continue with userdefined task, then another Task.Delay(y ms) and repeat from the user defined task.

var result = Task.Delay(initialDelay)
              .ContinueWith(t => dostuff..)
              .ContinueWith what goes here?

Is it even doable using tasks?

I could spin up a timer and be done with it, but using task seems to be the right way to go if I need cancellation, no?

Roger Johansson
  • 22,764
  • 18
  • 97
  • 193

1 Answers1

10

await makes this super easy:

public async Task TimedLoop(Action action, 
    CancellationToken token, TimeSpan delay)
{
    while (true)
    {
        token.ThrowIfCancellationRequested();
        action();
        await Task.Delay(delay, token);
    }
}

Without async (but still just using the TPL) it's a bit messier. I generally solve this problem by having a continuation that attaches itself to a variable of type Task. This works fine, but it can take a second to wrap your head around it. Without await it may be easier to just use a Timer instead.

public Task TimedLoop(Action action,
    CancellationToken token, TimeSpan delay)
{
    //You can omit these two lines if you want the method to be void.
    var tcs = new TaskCompletionSource<bool>();
    token.Register(() => tcs.SetCanceled());

    Task previous = Task.FromResult(true);
    Action<Task> continuation = null;
    continuation = t =>
    {
        previous = previous.ContinueWith(t2 => action(), token)
            .ContinueWith(t2 => Task.Delay(delay, token), token)
            .Unwrap()
            .ContinueWith(t2 => previous.ContinueWith(continuation, token));
    };
    previous.ContinueWith(continuation, token);
    return tcs.Task;
}
Servy
  • 202,030
  • 26
  • 332
  • 449
  • Seems like a good use-case for misusing iterators to simulate async. There's a library for that. – usr Feb 26 '14 at 18:10
  • @usr, an interesting idea about misusing `yield`. What's the name of that library? – noseratio Feb 26 '14 at 23:09
  • @Noseratio multiple people have hacked on that idea: https://www.google.com/webhp?complete=1&hl=en#complete=1&hl=en&q=.net+async+iterator I have implemented such a thing as well privately. – usr Feb 26 '14 at 23:10
  • @usr, a nice use of `ContinueWith`/`Unwrap`, although there's no `Task.FromResult` in .NET 4.0. The closest thing would probably be `TaskCompletionSource` with instant `SetResult`. – noseratio Feb 26 '14 at 23:18
  • @Noseratio [It's pretty trivial to implement in 4.0 if you need it.](http://stackoverflow.com/questions/14244114/create-a-completed-task/14244239#14244239) – Servy Feb 27 '14 at 01:09
  • What should be the object returned for user cancellation? CancellationTokenSource ? – Roger Johansson Feb 27 '14 at 09:29