Ayende @ Rahien

It's a girl

C# Async programming pitfall

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.

Comments

Daan Le Duc
05/19/2014 09:24 AM by
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
05/19/2014 09:34 AM by
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
05/19/2014 10:23 AM by
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.

njy
05/19/2014 10:24 AM by
njy

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

tobi
05/19/2014 11:46 AM by
tobi

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
05/19/2014 01:33 PM by
Matt Warren

@Bartosz

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

http://stackoverflow.com/questions/9037879/explicitly-specifying-the-taskscheduler-for-an-implicitly-scheduled-method/9038014#9038014

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
05/19/2014 01:43 PM by
Bartosz Adamczewski

@Matt

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
05/19/2014 01:45 PM by
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
05/19/2014 01:59 PM by
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
05/26/2014 03:52 AM by
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
05/26/2014 05:22 AM by
Ayende Rahien

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

Rene Stein
05/27/2014 01:15 PM by
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
06/05/2014 08:06 PM by
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
06/06/2014 06:03 AM by
Ayende Rahien

Jiri, Not it doesn't show that.

Jiri Cincura
06/06/2014 06:07 AM by
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.

Comments have been closed on this topic.