Kyle Delaney
Kyle Delaney

Reputation: 12264

How do async and await replace existing approaches?

From https://msdn.microsoft.com/en-us/library/mt674882.aspx#Threads:

The async-based approach to asynchronous programming is preferable to existing approaches in almost every case. In particular, this approach is better than BackgroundWorker for IO-bound operations because the code is simpler and you don't have to guard against race conditions. In combination with Task.Run, async programming is better than BackgroundWorker for CPU-bound operations because async programming separates the coordination details of running your code from the work that Task.Run transfers to the threadpool.

It seems to me that a chain of async functions must eventually end in waiting for something to happen that is outside of your program's control. Some Internet download or user input or something.

What about situations when your program must perform some lengthy calculation? It would have to be in a method that doesn't itself use await, because there's nothing to wait for when it's doing all the work itself. If await is not used then control wouldn't return back to the calling function, correct? If that's the case then surely it's not even asynchronous at all.

It seems BackgroundWorker is well-suited for lengthy calculations: https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx#Examples

Is there any way to use async/await for that purpose?

Upvotes: 2

Views: 1997

Answers (2)

Ghasan غسان
Ghasan غسان

Reputation: 5857

The key is here:

In combination with Task.Run, async programming is better than BackgroundWorker for CPU-bound operations because async programming separates the coordination details of running your code from the work that Task.Run transfers to the threadpool.

For a CPU-bound work (or IO-bound, it does not matter), Task.Run will run your code in a separate thread from thread pool, so the current code can await it, because it is running on a different thread. From the perspective of the second thread, the work is synchronous, but from the perspective of the first thread, the work is asynchronous.

The second thing is the coordination context. I have not used BackgroundWorker, but from my understanding of the text, it will need you to manually check if the work is done, and then retrieve the results, or maybe propagate exceptions, and so on. In async/await approach, all of this is covered for you.

All in all, it seems that async/await approach is a more friendly readable way of doing things compared to BackgroundWorker way.

Edit:

You can't use await on non-awaitable methods. A CPU-bound task can never be truly asynchronous, because something needs to be calculated, and a thread is needed for that. Either the current thread will do the calculation (blocking), or will give it to another background thread, so the current thread is not blocking and asynchronous.

If you are familiar with Node.js, you will notice that it is quite tricky to handle CPU-bound tasks, and often times you will need to refactor your code.

However, as user, your current thread is asynchronous regardless of whether the method you are awaiting is truly asynchronous or using another thread.

Upvotes: 2

Stephen Cleary
Stephen Cleary

Reputation: 456477

It seems to me that a chain of async functions must eventually end in waiting for something to happen that is outside of your program's control.

Yes; pure async code is generally used with I/O or other "events" (e.g., timers, notifications, or - less commonly - user input).

What about situations when your program must perform some lengthy calculation?

This is not the primary use case of async. However, it is a great use case for parallel computation (Parallel / PLINQ), or if the work is smaller, the thread pool (Task.Run).

If await is not used then control wouldn't return back to the calling function, correct? If that's the case then surely it's not even asynchronous at all.

That's correct; an async method without await would in fact run synchronously. And in fact, if you type that into Visual Studio, the compiler will give you a warning that actually says "this method will run synchronously". Because most of the time, that's a mistake.

If you want to run CPU-bound code on a thread pool thread, then use Task.Run. You can (and usually should) await the task returned by Task.Run, which allows the caller to treat the thread pool work as though it were asynchronous (even though it's really just running synchronously on a background thread):

await Task.Run(() => ...);

If you want the full power of parallel processing, you can just wrap that up inside the Task.Run:

await Task.Run(() => Parallel.ForEach(...));

It seems BackgroundWorker is well-suited for lengthy calculations

Not really. Retrieving results is a bit awkward, since it's easy to miss errors, and the results aren't type-safe. Also, coordinating multiple BGWs quickly becomes difficult. I have a blog series that shows how BackgroundWorker should essentially be considered obsoleted by Task.Run.

Upvotes: 1

Related Questions