More async and task questions

Hi, I have a few questions about the async and the tpl and hope you guys could shed some light on it.

​

So, if I have an async method FooAsync() that returns Task<T> does that mean that all functions/methods that call my FooAsync() will need to be an async method as well? Let's say Duck() calls FooAsync() does that mean I have to rewrite Duck() into DuckAsync() that awaits FooAsyn()? Also will any method that calls Duck() need to be converted into their async version? Where does it end? And what is the proper way of using async in this method call chain?

​

My second question is, if I am working with an API that uses the old async pattern (Asynch Programming Model) does that mean I can't use the new async await mechanisms along with it?

​

Third, If I want a background thread that checks status and performs it's own utility process continuously alongside my main app would I use Task.Run() or any of the TPL or would creating a class that contains it's own thread field and static fields to manage the thread be better to serve my purpose?

9 comments · 74% upvoted · other discussions

comments

tweq

So, if I have an async method FooAsync() that returns Task<T> does that mean that all functions/methods that call my FooAsync() will need to be an async method as well?

Ideally, yes. The main benefit of async is that it doesn't have to block a thread while waiting for something, if you wait on a task synchronously this benefit is largely lost. There is also a risk of deadlocks if your async method doesn't use ConfigureAwait(false).

Asynch Programming Model

There is a helper method for this purpose.

The docs look like a mess because it needs fairly flexible overloads, but you can look at some usage examples.

Third, If I want a background thread that checks status and performs it's own utility process continuously alongside my main app would I use Task.Run()

The reason why Task.Run is discouraged for long running operations is because it uses the thread pool, which expects to get its thread back reasonably quickly and will hesitate to start new threads leading to potential scaling/performance problems. In practice, doing it once probably won't have any noticeable effect, though.

[deleted]

Actually there's a minor performance gain to having methods that simply return a task, simply return a task. Don't make them async at all. Otherwise you're spot on as usual :)

[deleted]
[deleted]

Indeed :) As with everything, you do the right thing in the right situation as there's never a one size fits all solution.

Is this yours? The ConfigureAwait section is TBD. You can fill it in by saying (keeping in mind the above) use ConfigureAwait(false) for all the things :)

[deleted]

Not mine, but David Fowlers, from the asp.net team. The guy who created SignalR. He knows async pretty well. 😉

[deleted]

He does :) Did he do the first version of ASP.NET MVC too? It probably doesn't matter :)

[deleted]

Worked some on it, as far as I can tell. https://www.linkedin.com/in/davidfowl

[deleted]

If you want to reap the benefits of async code, you need async all the way to the top. C# 7.1 allows an async main, so there's no reason not to. Of course, if you're doing a GUI desktop app there's a lot of care to be taken around thread contexts, but that's a separate issue.

​

It's fairly easy to wrap old callback-based async code. What you'll want to look at is the TaskCompletionSource.

​

Whether you run the task from it's own class or not, does not matter a lot, I think (I personally prefer to push status updates from the worker process). If it's going to be long-lived, make sure to use the appropriate flags. Consult David Fowlers excellent list to make sure you aren't falling into any less-than-obvious traps.

Slypenslyde

The async keyword tells the compiler you plan to use the await keyword somewhere in the method. If you don't use await I think it's a compiler warning. You can call a method marked async without using await; if you do, it returns a Task or Task<T> and you're expected to do something with that.

So in summary: you only need to add async if you use await.

Second question:

There are idioms and patterns for converting older async patterns to the Task pattern. But await only works with async methods. So in general you have to make a new class that hides the old async pattern behind a method that returns a Task<T>.

Third question:

Don't use tasks or thread pool threads for long-running things. Use Thread. Tasks are expected to complete.

Personally: I don't use await which means I never have to worry about async. Or a lot of other baggage you can read in thousands of articles about how the most intuitive ways to use async and await can hinder performance, cause deadlocks, and lead to unobservable exceptions.