7

Several times, I have found myself writing long-running async methods for things like polling loops. These methods might look something like this:

private async Task PollLoop() { while (this.KeepPolling) { var response = await someHttpClient.GetAsync(...).ConfigureAwait(false); var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); // do something with content await Task.Delay(timeBetweenPolls).ConfigureAwait(false); } } 

The goal of using async for this purpose is that we don't need a dedicated polling thread and yet the logic is (to me) easier to understand than using something like a timer directly (also, no need to worry about reentrance).

My question is, what is the preferred method for launching such a loop from a synchronous context? I can think of at least 2 approaches:

var pollingTask = Task.Run(async () => await this.PollLoop()); // or var pollingTask = this.PollLoop(); 

In either case, I can respond to exceptions using ContinueWith(). My main understanding of the difference between these two methods is that the first will initially start looping on a thread-pool thread, whereas the second will run on the current thread until the first await. Is this true? Are there other things to consider or better approaches to try?

4
  • 1
    In the first one I see 3 tasks.. 1) Task.Run 2) because of await(generated by compiler) 3) PollLoop itself. On the other hand second one has only 1 Task. Commented Jul 12, 2014 at 19:50
  • If you reduce the first one to 2 task version, you can see the answer better: Task.Run(() => this.PollLoop().Wait()) Commented Jul 12, 2014 at 20:03
  • If this is a server-side code, you wouldn't need Task.Run anyway. Commented Jul 13, 2014 at 14:57
  • But if this task has a lot of code and is started from a windows service, the service start will wait for the await... and it may take the service long time to run.. so a trick is just to put await Task.Delay(10); on the beginning Commented Mar 11, 2022 at 15:08

2 Answers 2

7

My main understanding of the difference between these two methods is that the first will initially start looping on a thread-pool thread, whereas the second will run on the current thread until the first await. Is this true?

Yes. An async method returns its task to its caller on the first await of an awaitable that is not already completed.

By convention most async methods return very quickly. Yours does as well because await someHttpClient.GetAsync will be reached very quickly.

There is no point in moving the beginning of this async method onto the thread-pool. It adds overhead and saves almost no latency. It certainly does not help throughput or scaling behavior.

Using an async lambda here (Task.Run(async () => await this.PollLoop())) is especially useless. It just wraps the task returned by PollLoop with another layer of tasks. it would be better to say Task.Run(() => this.PollLoop()).

Sign up to request clarification or add additional context in comments.

4 Comments

Infact, Task.Run(() => this.PollLoop()) is also useless. this.PollLoop() is enough...
@EZI kind of. He asks especially about that and I discuss this in my answer.
You discuss it but say Task.Run(() => this.PollLoop()) would be better which creates a reduntant task araound PollLoop. Calling PollLoop(); without await will already run in a Task.
@EZI Well, one level of redundancy is better than two levels of redundancy! :)
3

My main understanding of the difference between these two methods is that the first will initially start looping on a thread-pool thread, whereas the second will run on the current thread until the first await. Is this true?

Yes, that's true.

In your scenario, there seem to be no need for using Task.Run though, there's practically no code between the method call and the first await, and so PollLoop() will return almost immediately. Needlessly wrapping a task in another task only makes the code less readable and adds overhead. I would rather use the second approach.

Regarding other considerations (e.g. exception handling), I think the two approaches are equivalent.

The goal of using async for this purpose is that we don't need a dedicated polling thread and yet the logic is (to me) easier to understand than using something like a timer directly

As a side-note, this is more or less what a timer would do anyway. In fact Task.Delay is implemented using a timer!

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.