Kevin Kevin - 3 years ago 117 Question

VB.NET: What happens if I run CPU-bound code using Await?

I am trying to understand async/await. I understand that you should not Await a CPU-bound method, but to help my understanding I am curious what happens if you do. Consider:

Public Async Function DoSomeTasks()
Await LongRunningCPUBoundMethod1()

End Function

Public Async Function LongRunningCPUBoundMethod1() As Task
' Do stuff synchronously
End Function

Public Sub LongRunningCPUBoundMethod2()
' Do stuff synchronously
End Sub

How will the Task Scheduler handle the CPU resources? In what order will these methods execute? Will LongRunningCPUBoundMethod1 or LongRunningCPUBoundMethod2 execute first?

Answer Source

The thing to remember here is that Async/Await code is not necessarily multi-threaded. You can use them to help with multi-threaded code by awaiting items that start a separate thread, but what they really do is allow you to break up several tasks efficiently in the same thread.

This doesn't come without some overhead; switching between asynchronous tasks has a cost. When you await cpu-bound tasks, you've added that cost to the already cpu-intensive work, and therefore made things worse rather than better. However, if you combine this with code that starts the cpu-heavy tasks in a separate thread, and then uses a WaitHandle or a Task to send the results back, you might be fine again (depending on how many items you're awaiting relative to the number of available cores), because now you're taking advantage of the multiple cores in your CPU.

Additionally, let's look at this in context of .Net WinForms. It's important to remember here that you never want to do significant CPU work on main UI thread. Really, anything that blocks for more than a few milliseconds is problematic. If that thread is busy, the Windows Message pump doesn't run, you can't respond to events, and your user interface becomes unresponsive.

To understand Await in this context, think of it as if it breaks your method up into two parts (or more, if there is more than one Await). Everything up to and including the line with Await runs immediately, and everything after the await is hidden away by the compiler in a new callback method (called a continuation) that will be called with the same context (including variables local to the original method) and in the same thread when the Await has finished.

With this information, it should be clear that if you directly Await a cpu-bound method, you're still doing that work immediately on the UI thread, and your user interface is still in trouble. However, you can again account for this by starting the cpu-bound method in it's own thread. Await, in conjunction with Tasks, make this relatively easy to do without having to write a lot of new code. Certainly it's much better than the old DoEvents() technique.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download