How to Call Asynchronous Method from Synchronous Method in C#

How to call asynchronous method from synchronous method in C#?

Asynchronous programming does "grow" through the code base. It has been compared to a zombie virus. The best solution is to allow it to grow, but sometimes that's not possible.

I have written a few types in my Nito.AsyncEx library for dealing with a partially-asynchronous code base. There's no solution that works in every situation, though.

Solution A

If you have a simple asynchronous method that doesn't need to synchronize back to its context, then you can use Task.WaitAndUnwrapException:

var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();

You do not want to use Task.Wait or Task.Result because they wrap exceptions in AggregateException.

This solution is only appropriate if MyAsyncMethod does not synchronize back to its context. In other words, every await in MyAsyncMethod should end with ConfigureAwait(false). This means it can't update any UI elements or access the ASP.NET request context.

Solution B

If MyAsyncMethod does need to synchronize back to its context, then you may be able to use AsyncContext.RunTask to provide a nested context:

var result = AsyncContext.RunTask(MyAsyncMethod).Result;

*Update 4/14/2014: In more recent versions of the library the API is as follows:

var result = AsyncContext.Run(MyAsyncMethod);

(It's OK to use Task.Result in this example because RunTask will propagate Task exceptions).

The reason you may need AsyncContext.RunTask instead of Task.WaitAndUnwrapException is because of a rather subtle deadlock possibility that happens on WinForms/WPF/SL/ASP.NET:

  1. A synchronous method calls an async method, obtaining a Task.
  2. The synchronous method does a blocking wait on the Task.
  3. The async method uses await without ConfigureAwait.
  4. The Task cannot complete in this situation because it only completes when the async method is finished; the async method cannot complete because it is attempting to schedule its continuation to the SynchronizationContext, and WinForms/WPF/SL/ASP.NET will not allow the continuation to run because the synchronous method is already running in that context.

This is one reason why it's a good idea to use ConfigureAwait(false) within every async method as much as possible.

Solution C

AsyncContext.RunTask won't work in every scenario. For example, if the async method awaits something that requires a UI event to complete, then you'll deadlock even with the nested context. In that case, you could start the async method on the thread pool:

var task = Task.Run(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();

However, this solution requires a MyAsyncMethod that will work in the thread pool context. So it can't update UI elements or access the ASP.NET request context. And in that case, you may as well add ConfigureAwait(false) to its await statements, and use solution A.

Update, 2019-05-01: The current "least-worst practices" are in an MSDN article here.

What is the best practice to call Async method from Sync method?

what the best practice would be?

There is no best practice, and here's why:

Every hack works in some situations and does not work in other situations. There is no hack that works in all situations. If there was a hack that worked everywhere, then that would be what everyone would use and that hack would be the best practice. But there isn't a hack that works everywhere, so there is no "best practice". There is no general-purpose solution.

The various hacks are described in this article, along with a description of the situations where each one works and doesn't.

Confusion on Execution of Asynchronous method inside the Synchronous method

I don't want this execution to wait synchronously or asynchronously. So, I haven't used the await keyword or wait method.

"Fire and forget" on ASP.NET is dangerous, as you have discovered.

Technically, "fire and forget" means "forget". If you fire off a task without waiting synchronously or asynchronously, then your code is explicitly saying it doesn't care when that task completes, or how it completes, or if it completes.

Async method got executed some portion and some just skipped the execution... Now, I got the execution but want to understand why this async and method behaving random execution inside the synchronous method.

The bottom line is that shutdowns are normal. If you send a response to an HTTP request, then as far as ASP.NET knows, that request has been handled; and if there are no other requests, then it is safe to shut down. Fire-and-forget code is request-extrinsic (outside the scope of an HTTP request), so ASP.NET isn't even aware that that code exists.

I feel CLR should be responsible to execute these remaining portion of code.

Nope. The CLR can't do this because that would prevent some use cases regarding Task.WhenAny.

If you need to return early, the proper solution is:

  1. Add a durable queue to your architecture. E.g., an Azure Queue.
  2. Add a separate background processor to your architecture. E.g., an Azure Function.

Then, when your ASP.NET app wants to return early, it should serialize the work it needs to do into a message and write that to the durable queue, and then return. The separate background processor should read messages from the queue and do the actual work.

Wrapping synchronous code into asynchronous call

It's important to make a distinction between two different types of concurrency. Asynchronous concurrency is when you have multiple asynchronous operations in flight (and since each operation is asynchronous, none of them are actually using a thread). Parallel concurrency is when you have multiple threads each doing a separate operation.

The first thing to do is re-evaluate this assumption:

The method itself is synchronous call to the service and there is no possibility to override the implementation.

If your "service" is a web service or anything else that is I/O-bound, then the best solution is to write an asynchronous API for it.

I'll proceed with the assumption that your "service" is a CPU-bound operation that must execute on the same machine as the web server.

If that's the case, then the next thing to evaluate is another assumption:

I need the request to execute faster.

Are you absolutely sure that's what you need to do? Are there any front-end changes you can make instead - e.g., start the request and allow the user to do other work while it's processing?

I'll proceed with the assumption that yes, you really do need to make the individual request execute faster.

In this case, you'll need to execute parallel code on your web server. This is most definitely not recommended in general because the parallel code will be using threads that ASP.NET may need to handle other requests, and by removing/adding threads it will throw the ASP.NET threadpool heuristics off. So, this decision does have an impact on your entire server.

When you use parallel code on ASP.NET, you are making the decision to really limit the scalability of your web app. You also may see a fair amount of thread churn, especially if your requests are bursty at all. I recommend only using parallel code on ASP.NET if you know that the number of simultaneous users will be quite low (i.e., not a public server).

So, if you get this far, and you're sure you want to do parallel processing on ASP.NET, then you have a couple of options.

One of the easier methods is to use Task.Run, very similar to your existing code. However, I do not recommend implementing a CalculateAsync method since that implies the processing is asynchronous (which it is not). Instead, use Task.Run at the point of the call:

private async Task MakeRequest()
{
// do some stuff
var task = Task.Run(() => Calculate(myInput));
// do other stuff
var myOutput = await task;
// some more stuff
}

Alternatively, if it works well with your code, you can use the Parallel type, i.e., Parallel.For, Parallel.ForEach, or Parallel.Invoke. The advantage to the Parallel code is that the request thread is used as one of the parallel threads, and then resumes executing in the thread context (there's less context switching than the async example):

private void MakeRequest()
{
Parallel.Invoke(() => Calculate(myInput1),
() => Calculate(myInput2),
() => Calculate(myInput3));
}

I do not recommend using Parallel LINQ (PLINQ) on ASP.NET at all.

Does running a synchronous method using Task.Run() make it asynchronous

Here the method I've written to create a dictionary out of my List, does it run asynchronously?

It's probably more accurate to say it runs in parallel. A separate thread is created to produce the result. If people isn't being used in a thread-safe way (e.g. it's a List that you're adding to in other threads), then you could end up with messy race conditions.

When people talk about "Asynchronous" code, there are two things they could mean.

  1. Code that models an inherently asynchronous operation, like Disk or Network I/O, in a way that doesn't block a thread.
  2. More broadly, any kind of operation that can complete independently from the current thread of operations.

What you've done matches the second definition, but not the first. If people were backed by a database round-trip, your code would still block a thread while that call completed: it would just be blocking a different thread than the one that calls MapDepartments.

What's the difference between this and putting my dictionary logic in an asynchronous method?

Since you're pulling from an in-memory List, and all your operations are CPU-bound (not doing truly asynchronous operations), there would be no benefit to putting your logic in an async method, because there would be nothing for that method to await. Even though the method would say it's async, it would run synchronously to completion before returning a Task.

Assuming creating that dictionary really takes enough time to make it worth multi-threading, and assuming you're not modifying the underlying list before you await the Task, your current approach is better.

Whatever I've implemented is this somewhat similar to having a .ToDictionaryAsync() method?

I mean, I guess you could write a ToDictionaryAsync method that just performs ToDictionary on a separate thread, but that's really not following the spirit of what Async means.

When a library like Entity Framework provides a ToDictionaryAsync() method, that's because they know there's going to be an actual database call that will actually be asynchronous, and they know how to make that happen in a non-blocking way without calling ToDictionary(). I would reserve ToDictionaryAsync for situations like that.



Related Topics



Leave a reply



Submit