2

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?

4
  • 1
    BackgroundWorker usage can be converted to a few or a single Task and then you can await on them. Commented Jan 28, 2017 at 2:57
  • I started to write an answer but realized that the answer would be too long and too broad for the forum. But in short: The concept of asynchronous programming can be a bit difficult to understand. One can benefit from asynchronous programming in many cases but not all. GUI is a perfect example where asynchronous programming works very well. Chess, not so much. I'd suggest you read some more literature on the topic to get a deeper understanding. Commented Jan 28, 2017 at 3:06
  • 1
    async without await is really just synchronous Commented Jan 28, 2017 at 3:21
  • Sani, I'm reading more literature every day but I still hadn't seen this question addressed. Why does asynchronous programming not work with chess? Commented Jan 29, 2017 at 3:27

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

3 Comments

So the solution is to just create a task normally and await it, rather than only awaiting tasks returned by async functions? Here I was thinking about using async without await, but I should've been thinking about using await without async.
Yeah, if you want to run some task in the back ground, then just use Task.Run. Using asyc without await effectively makes it synchronous, and you cannot use await without async, because await is a contextual keyword that is treated as an identifier except if an async modifier is present.
Okay, what i meant when I said await without async isn't that I'd use the await keyword in a non-async function, but that I'd await something that didn't itself come from an async function.
1

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.

2 Comments

@Kyle - Stephen's There is no thread post is also a good read re this: "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", as it's important to grasp the benefit this gives for I/O-bound operations.
Hmm, when I said BackgroundWorker is well-suited, I just meant the documentation makes it look like that sort of thing is what BackgroundWorker was made for, in contrast to async/await which apparently wasn't designed with lengthy calculations in mind.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.