Ayende @ Rahien

My name is Oren Eini
Founder of Hibernating Rhinos LTD and RavenDB.
You can reach me by phone or email:


+972 52-548-6969

, @ Q c

Posts: 6,125 | Comments: 45,488

filter by tags archive

C# Async programming pitfall

time to read 2 min | 233 words

I really like the TPL, and I really like the async/await syntax. It is drastically better than any other attempt I’ve seen to handle concurrency.

But it also has a really major issue. There is no way to properly debug things. Imagine that I have the following code:

public Task DoSomething()
return new TaskCompletionSource<object>().Task;

And now imagine that I have some code that is going to do an await DoSomething();

What is going to happen? This is a never ending task, so we’ll never return. And that is fine, except that there is absolutely no way to see that. I have no way of seeing which task didn’t return, and I have no way of seeing all the pending tasks, and what they are all waiting for, etc. I’ve run into something like that (obviously a lot harder to figure out) too many times.

If I was using non async code, it would be obvious that there is this thread that is stopped on this thing, and I could figure it out. For us, this make it a lot harder to work with.


Daan Le Duc

Thats the reason why i usually avoid the TaskCompletionSource and use the .net 4.5 feature Task.FromResult();

I Agree that there's no proper tooling for the async stuff

Bartosz Adamczewski

If you want to be serious about concurrency then you should skip async code and fine control your threads. TPL is fine when used as a lib but still it provides minimal information what's going on and that's the point to some extent but for e.g you have very little control (zero if im not mistaken) to control the work queues and pick strategies, that your app could benefit from.

Paul Turner

This is one of my biggest issues with the Parallel Tasks window in Visual Studio - it neglects to show certain flavours of tasks, making it entirely useless: I need a reliable picture of all the tasks I might be waiting on in order to understand the state of the system, regardless of whether they're waiting on an IO-completion callback, a delay or simply to be scheduled on a task scheduler.

I typically find the Threads window to be much more help, since I can see the callstack and get a suggestion of the tasks it might be blocked on. It's not where we should be with this tooling though.


@Oren: did any of the other technologies/approaches solve these problems?


You have no current stack and no stack trace.

If you have to use async IO, use await. But if not, don't use it at all.

This shows all the symptoms of a fad. I see lots of small websites go async which is a terrible choice with no benefits and noticeable harm. Asyncing the database is also often not a good idea even for big websites.

Matt Warren


You can control things, you just create you own TaskScheduler (inherit from System.Threading.Tasks.TaskScheduler) and then launch tasks on that instead:


You could even make the default, via a custom SynchronisationContext (http://msdn.microsoft.com/en-us/library/system.threading.synchronizationcontext.setsynchronizationcontext.aspx), although I don't know if that is a good idea or not ;-)

Bartosz Adamczewski


I'm aware that you can create your own task scheduler but then you need to code everything yourself with some boundaries that come from TPL itself (in that case why use tasks at all?). The default scheduler uses a .NET 4.0 TP which uses work queues and work stealing all which operate on a strategy that could be easily configured but that's impossible nor you can get any relevant information about the queue states, workloads etc.

That's the problem all relevant information and fine grained control is taken away from you.

Matt Warren

The call stack issue is because call-stacks are really about "where you are going to next", not "where you've just been" which is what you want when debugging. See http://stackoverflow.com/questions/6595473/the-call-stack-does-not-say-where-you-came-from-but-where-you-are-going-next/6597522#6597522

In synchronous code these 2 things are the same, but that's not with async. This blog post has some nice ideas of a work-around http://blog.stephencleary.com/2013/05/announcement-async-diagnostics.html. But it requires PostSharp to re-write you code and you have to add other manual code in, so it's far from ideal.

Marcus King

When you abstract away difficulty to make things easier on the developer, you wind up with issues like this. The more you expose, the harder it becomes, and the less people are willing to learn it. They devolve into my 4yr old trying to learn to play Cut the Rope. Also, VS has never been great for async development.

Stephen Cleary

The easiest way to avoid this pitfall is to minimize the method that call TaskCompletionSource. If you do need to use it, make a small extension method that uses it following the standard pattern.

The tooling could be better, but that's a surprisingly difficult pattern. In particular, the notion of "tasks awaiting tasks" is a nice concept but that's not what actually happens.

Ayende Rahien

Stephen, The TaskCompletionSource was an example. Any long TPL would cause the same exact behavior.

Rene Stein

I am playing with the idea of the broken promise (aka TaskCompletionSource) in DEBUG build.

DebugTaskCompletionSource. https://bitbucket.org/renestein/rstein.async/src/3878dec1fcc0e796619f82344843e207343d7d21/RStein.Async/Tasks/DebugTaskCompletionSource.cs?at=master

Example: Leaking unfulfilled TCS. https://bitbucket.org/renestein/rstein.async/src/3878dec1fcc0e796619f82344843e207343d7d21/RStein.Async.Examples/BrokenPromise/LeakTaskCompletionSource.cs?at=master

testBrokenPromises. https://bitbucket.org/renestein/rstein.async/src/3878dec1fcc0e796619f82344843e207343d7d21/RStein.Async.Examples/Program.cs?at=master#cl-38

Jiri Cincura

Have you checked the Parallel Tasks window? It doesn't show you exactly what you want. But it gives you idea what's wrong.

Ayende Rahien

Jiri, Not it doesn't show that.

Jiri Cincura

Sure, it's not exactly that. But between two points in time you can compare tasks you're interested in and see how it's progressing. Still manual work and PIA, but at least something.

Comment preview

Comments have been closed on this topic.


  1. The design of RavenDB 4.0: Physically segregating collections - 10 hours from now
  2. RavenDB 3.5 Whirlwind tour: I need to be free to explore my data - about one day from now
  3. RavenDB 3.5 whirl wind tour: I'll have the 3+1 goodies to go, please - 4 days from now
  4. The design of RavenDB 4.0: Voron has a one track mind - 5 days from now
  5. RavenDB 3.5 whirl wind tour: Digging deep into the internals - 6 days from now

And 12 more posts are pending...

There are posts all the way to May 30, 2016


  1. The design of RavenDB 4.0 (14):
    03 May 2016 - Making Lucene reliable
  2. RavenDB 3.5 whirl wind tour (14):
    04 May 2016 - I’ll find who is taking my I/O bandwidth and they SHALL pay
  3. Tasks for the new comer (2):
    15 Apr 2016 - Quartz.NET with RavenDB
  4. Code through the looking glass (5):
    18 Mar 2016 - And a linear search to rule them
  5. Find the bug (8):
    29 Feb 2016 - When you can't rely on your own identity
View all series


Main feed Feed Stats
Comments feed   Comments Feed Stats