This is a fairly standard piece of code, which does a “complex” async process and then move on. It is important in this case to do the operation in the order they were given, and the real code is actually doing something that need to be async (go and fetch some data from a remote server).

It is probably easier to figure out what is going on when you look at the C# 5.0 code:

I played with user mode scheduling in .NET a few times in the past, and one of the things that I was never able to resolve properly was the issue of the stack depth. I hoped that the TPL would resolve it, but it appears that it didn’t. Both code samples here will throw StackOverFlowException when run.

It sucks, quite frankly. I understand why this is done this way, but I am quite annoyed by this. I expected this to be solved somehow. Using C# 5.0, I know how to solve this:

Comments

You'd have to do a similar translation to the one done by syntactic sugar in C# 5, which is quite a complicated translation. I'd just look at what a decompiler outputs and base my code on that, probably.

First, you can use the C# 5 compiler with .NET 4.0. Alex Davies and I adapted some code written by Daniel Grunwald into a library called AsyncBridge. It's similar to LinqBridge in how it allows you to use the new async features with .NET 4 & .NET 3.5. You can get the nuget (https://nuget.org/packages/AsyncBridge) or the source (https://github.com/OmerMor/AsyncBridge).

actually I'd like to repro this because I have such code in production. Under what circumstances will this cause a StackOverflow? I tried x86/x64 cross-product debug/(release without debugger) with 1000 * 1000. Works correctly.

tobi,
This is the code using a Console Application, .NET Client Profile 4.0 with x86.
It is possible that you need to replace the math stuff with an actual blocking call, like a web request to reproduce that.

If you're using ContinueWith with the default TaskContinuationOptions, then there really should be no recursion, as the continuation will execute on a different thread (or the current one, after it returns and the stack unwinds). At least for the "server" profile they solve the recursion issue by using stack probes (via the Win32 api).

I still fail to grasp the difference - once you've given out the ball (instructions to run a task with a loop and bunch of calls) to TPL, how is it different for your main thread how this async task executes?

Bunter,
Let us say that I have only 2 threads in my system.
If I am holding one waiting for an async operation, that is going to cause a lot of waiting, and will holdup the entire thread.
If I am using async operations, I am NOT holding a thread while the OS is doing the actual IO

This query will run the async operation one at a time, in the right order.
For explanation - see this thread in the Rx forum (where I posted your question): http://social.msdn.microsoft.com/Forums/en-US/rx/thread/f7171f3b-523a-4df7-a134-d7bf633cdc55.

Bunter, for caller there is no difference how asynchronous call executes. But for whole system creation of new threads is very expensive operation. Async IO helps handle massive coucurrency with small number of threads.