0

Aesthetic question really.

Given this code (polling unavoidable):

protected override async Task ExecuteAsync(CancellationToken ct) { // move the loop here somehow? await Task.WhenAll( Task.Run(async () => await this.PollA(ct), ct), Task.Run(async () => await this.PollB(ct), ct), Task.Run(async () => await this.PollC(ct), ct)) .ConfigureAwait(false); } 

the polling methods look like this at the moment, each one has a different delay.

private async Task Poll(CancellationToken ct) { while (!ct.IsCancellationRequested) { await Task.Delay(Math.Max(1000, CONFIGA), ct); this._logger.StartAction("poll A status"); this._logger.StopAction("poll A status"); } } 

Is there a way to structure a continuation that removes the loop in each of the Poll methods

private async Task Poll(CancellationToken ct) { await Task.Delay(Math.Max(1000, CONFIGA), ct); this._logger.StartAction("poll A status"); this._logger.StopAction("poll A status"); } 

This might not even be the right pattern, but it seems better than having three infinite loops.

Task.WhenAny([A,B,C]) => // recreate any complete task as soon as it returns // and await the new "continuation"? 
1
  • 1
    Why not just await Task.WhenAll(this.PollA(ct), this.PollB(ct), this.PollC(ct))? But certainly you could just do while(!ct.IsCancellationRequested)) await Task.WhenAny(this.PollA(ct), this.PollB(ct), this.PollC(ct)) if that was the behaviour you wanted. Commented Dec 7, 2017 at 7:22

2 Answers 2

1

I have an Aesthetic solution, that is probably not advisable to be used since it will probably cause stack overflow eventually. It maybe demonstrates why the loop is a better option.

I must admit do not really understand your example in a real world context. In my mind almost all code that executes for a long time will do it in a finite loop, and thus to check for cancellation after each loop iteration sounds like a good idea to me.

Unless you want your code just to run infinitely until the task is canceled, in which case my aesthetic solution will probably cause a stack overflow if left to long, but it was fun none the less coming up with this code.

I created a Extension method:

public static class Extensions { public static async Task ContinueWithInfinitly(this Task task, Func<Task> continuationAction, CancellationToken cancellationToken) { await task; if (!cancellationToken.IsCancellationRequested) { var newTask = continuationAction.Invoke(); await newTask.ContinueWithInfinitly(continuationAction, cancellationToken); } } } 

Which Base on your code will then be called as follows:

await Task.WhenAll( Task.Run(async () => await this.PollA(ct).ContinueWithInfinitly(() => PollA(ct), ct)), Task.Run(async () => await this.PollB(ct).ContinueWithInfinitly(() => PollB(ct), ct)), Task.Run(async () => await this.PollC(ct).ContinueWithInfinitly(() => PollC(ct), ct))) .ConfigureAwait(false); 

Although I dont see the point of wrapping each method again in a Task.Run. So i can also just be

await Task.WhenAll( this.PollA(ct).ContinueWithInfinitly(() => PollA(ct), ct), this.PollB(ct).ContinueWithInfinitly(() => PollB(ct), ct), this.PollC(ct).ContinueWithInfinitly(() => PollC(ct), ct)) .ConfigureAwait(false); 
Sign up to request clarification or add additional context in comments.

3 Comments

mmh, that will work - though I suspect that the pattern is broken, and it's better to just use the loops... thanks. I think you're also correct about the inherent stack problems...
@Jim Yes it is broken, i think in a way this demonstrates why you should use loops because the alternative is worse
I thought that a custom Awaitable might do the trick, but I think it's also going to end up looking messy.
0

You can use Task.WhenAny like this:

private async Task<Tuple<string, int>> Poll(string type, int delay, CancellationToken ct) { await Task.Delay(Math.Max(1000, delay), ct); Console.WriteLine($"poll {type} status"); // return input arguments back return Tuple.Create(type, delay); } private async Task PollAll(CancellationToken ct) { var tasks = new [] { Poll("A", 3000, ct), Poll("B", 2000, ct), Poll("C", 1000, ct) }; while (!ct.IsCancellationRequested) { var completed = await Task.WhenAny(tasks); var index = Array.IndexOf(tasks, completed); // await to throw exceptions if any await completed; // replace with new task with the same arguments tasks[index] = Poll(completed.Result.Item1, completed.Result.Item2, ct); } } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.