Do the new C# 5.0 'async' and 'await' keywords use multiple cores?

C#AsynchronousParallel ProcessingMulticoreC# 5.0

C# Problem Overview


Two new keywords added to the C# 5.0 language are async and await, both of which work hand in hand to run a C# method asynchronously without blocking the calling thread.

My question is, do these methods actually take advantage of multiple cores and run in parallel or does the async method run in the same thread core as the caller?

C# Solutions


Solution 1 - C#

> Two new keywords added to the C# 5.0 language are async and await, both of which work hand in hand to run a C# method asynchronously without blocking the calling thread.

That gets across the purpose of the feature, but it gives too much "credit" to the async/await feature.

Let me be very, very clear on this point: await does not magically cause a synchronous method to run asynchronously. It does not start up a new thread and run the method on the new thread, for example. The method you are calling has to be the thing that knows how to run itself asynchronously. How it chooses to do so is its business.

> My question is, do these methods actually take advantage of multiple cores and run in parallel or does the async method run in the same thread core as the caller?

Again, that is entirely up to the method you call. All that await does is instruct the compiler to rewrite the method into a delegate that can be passed as the continuation of the asynchronous task. That is, the await FooAsync() means "call FooAsync() and whatever comes back must be something that represents the asynchronous operation that just started up. Tell that thing that when it knows that the asynchronous operation is done, it should call this delegate." The delegate has the property that when it is invoked, the current method appears to resume "where it left off".

If the method you call schedules work onto another thread affinitized to another core, great. If it starts a timer that pings some event handler in the future on the UI thread, great. await doesn't care. All it does is makes sure that when the asynchronous job is done, control can resume where it left off.

A question you did not ask but probably should have is:

> When the asynchronous task is finished and control picks up where it left off, is execution in the same thread as it was before?

It depends on the context. In a winforms application where you await something from the UI thread, control picks up again on the UI thread. In a console application, maybe not.

Solution 2 - C#

Eric Lippert has an excellent answer; I just wanted to describe async parallelism a bit further.

The simple "serial" approach is where you await just one thing at a time:

static void Process()
{
  Thread.Sleep(100); // Do CPU work.
}

static async Task Test()
{
  await Task.Run(Process);
  await Task.Run(Process);
}

In this example, the Test method will queue Process to the thread pool, and when it completes, it will queue Process again to the thread pool. The Test method will complete after ~200ms. At any time, only one thread is really moving the progress forward.

A simple way to parallelize this is to use Task.WhenAll:

static void Process()
{
  Thread.Sleep(100); // Do CPU work.
}

static async Task Test()
{
  // Start two background operations.
  Task task1 = Task.Run(Process);
  Task task2 = Task.Run(Process);

  // Wait for them both to complete.
  await Task.WhenAll(task1, task2);
}

In this example, the Test method queues Process to the thread pool twice, and then waits for them both to complete. The Test method will complete after ~100ms.

Task.WhenAll (and Task.WhenAny) were introduced with async/await to support simple parallelism. However, the TPL is still there if you need anything more advanced (true CPU-bound parallel processing is a better fit for the TPL). TPL plays well with async/await.

I cover basic async parallelism in my into to async blog post, as well as the "context" that Eric alluded to.

Solution 3 - C#

An async method returns an awaitable object (one that has a GetAwaiter method), and the compiler can generate code to consume that object if you call the method with the await keyword. You're also free to call such a method without the await keyword, and consume the object explicitly.

The object encapsulates an asynchronous action, which may or may not run on another thread. Eric Lippert's article Asynchrony in C# 5.0 part Four: It's not magic considers an example of asynchronous programming that involves only one thread.

Solution 4 - C#

Since async and await are based around the TPL, they should work very similarly. By default, you should treat them as if they run on a separate thread.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionIcemanindView Question on Stackoverflow
Solution 1 - C#Eric LippertView Answer on Stackoverflow
Solution 2 - C#Stephen ClearyView Answer on Stackoverflow
Solution 3 - C#phoogView Answer on Stackoverflow
Solution 4 - C#Kendall FreyView Answer on Stackoverflow