Calling Async Methods from Non-Async Code

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.

Calling async methods from non-async code

So I'm left with how to best call async methods in a synchronous way.

First, this is an OK thing to do. I'm stating this because it is common on Stack Overflow to point this out as a deed of the devil as a blanket statement without regard for the concrete case.

It is not required to be async all the way for correctness. Blocking on something async to make it sync has a performance cost that might matter or might be totally irrelevant. It depends on the concrete case.

Deadlocks come from two threads trying to enter the same single-threaded synchronization context at the same time. Any technique that avoids this reliably avoids deadlocks caused by blocking.

In your code snippet, all calls to .ConfigureAwait(false) are pointless because the return value is not awaited. ConfigureAwait returns a struct that, when awaited, exhibits the behavior that you requested. If that struct is simply dropped, it does nothing.

RunSynchronously is invalid to use because not all tasks can be processed that way. This method is meant for CPU-based tasks, and it can fail to work under certain circumstances.

.GetAwaiter().GetResult() is different from Result/Wait() in that it mimics the await exception propagation behavior. You need to decide if you want that or not. (So research what that behavior is; no need to repeat it here.) If your task contains a single exception then the await error behavior is usually convenient and has little downside. If there are multiple exceptions, for example from a failed Parallel loop where multiple tasks failed, then await will drop all exceptions but the first one. That makes debugging harder.

All these approaches have similar performance. They will allocate an OS event one way or another and block on it. That's the expensive part. The other machinery is rather cheap compared to that. I don't know which approach is absolutely cheapest.

In case an exception is being thrown, that is going to be the most expensive part. On .NET 5, exceptions are processed at a rate of at most 200,000 per second on a fast CPU. Deep stacks are slower, and the task machinery tends to rethrow exceptions multiplying their cost. There are ways of blocking on a task without the exception being rethrown, for example task.ContinueWith(_ => { }, TaskContinuationOptions.ExecuteSynchronously).Wait();.

I personally like the Task.Run(() => DoSomethingAsync()).Wait(); pattern because it avoids deadlocks categorically, it is simple and it does not hide some exceptions that GetResult() might hide. But you can use GetResult() as well with this.

Calling async method from non async method

You don't need the GettingModel(string vinText) method.
By calling the Task.Result you are blocking the main thread.

Calling .Result in the UI thread will likely deadlock everything which is what you are experiencing. Use ContinueWith or async void with await.

You can make your Entry_TextChanged async and await the web request so that it doesn't block the UI.

You can even run it on a separate thread and use ContinueWith() if you don't require to make the make user wait for the operation to complete. If you are going that route make sure you use Device.BeginInvookeOnMainThread() to run any code that needs to be run on UI thread.

The better code would be :

private async void Entry_TextChanged(object sender, TextChangedEventArgs e)
{
var newText = e.NewTextValue;
//once 3 keystroke is visible by 3
if (newText.Length % 3 == 0)
{
//Call the web services
var result = await GetModelStringForVIN(newText);
if (string.IsNullOrEmpty(result) == false)
{
ModelVIN.Text = result;
}
}
}

private async Task<string> GetModelStringForVIN(string vinText)
{
var deviceId = CrossDeviceInfo.Current.Model;
deviceId = deviceId.Replace(" ", string.Empty);
var requestMgr = new RequestManager(deviceId);

var VinData = await requestMgr.getModelForVIN(vinText);

return VinData?.Model;
}

The following links would help you understand the concepts better :

  1. Xamarin Async Support Overview
  2. Asynchronous Operations with Xamarin

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.

Add async in a non-async interfaces

You cannot change your interface to (optionally) support asynchronous calls in a way that maintains backward compatibility—and that’s a good thing.

Explanation

Remember, the entire purpose of an interface is to guarantee that all callers can interact with a concrete implementation of that interface without knowing anything about the concrete type itself. Implementing a method as async changes that method’s signature—i.e., by returning a Task<> and potentially expecting an await keyword, depending on how it’s called—and, therefore, breaks the interface. You can change the signature, but by definition that breaks backward compatibility with the existing interface.

Options

Given this, there are three textbook solutions to this problem:

  1. Handle the asynchronous calls within your existing synchronous interface, as suggested in @Neil's answer. This maintains backwards compatibility, but eliminates the benefits of asynchronous calls.
  2. Change the interface to use async, thus requiring all implementations to be updated. This is the most invasive approach, and may not be practical if you don't own all implementations.
  3. Create a new async version of your interface (e.g., IServiceAsync) which is used by implementations and callers requiring asynchronous functionality, as suggested in @Ryan Wilson’s comment.

If your priority is exclusively backward compatibility with existing code, and not the performance benefits of asynchronous processing, you should take the first option.

Assuming you want the performance benefits of asynchronous calls, however, the last option makes the most sense given that you are working with legacy code; that will be especially true if you don’t own all of the implementations.

The rest of this answer will assume the third option.

Base Interface

If appropriate, your synchronous and asynchronous interfaces can derive from a common interface which shares any non-async methods, thus allowing them to be used interchangeably in those scenarios. This is useful if you have code that doesn’t rely on any of the implicated async methods. Your question doesn't suggest that, but I'm assuming your interface may be more involved than what's included here.

Central Manager

Your central manager will need to be updated to look for both IService and IServiceAsync, and conditionally call e.g., RunAsync() on the latter. Just make sure you’re truly taking advantage of the asynchronous capabilities in that case (e.g., by adding these to a task queue and processing tasks as they complete). Otherwise, you won't gain any performance benefits from the asynchronous interface.

Impact

I recognize that your objective was to avoid updating your central manager. Unfortunately, though, there’s no way to accomplish this while also taking advantage of any asynchronous processing desired for the API call.

How Do I Call an Async Method from a Non-Async Method?

Am I missing something?

Yes. Asynchronous code - by its nature - implies that the current thread is not used while the operation is in progress. Synchronous code - by its nature - implies that the current thread is blocked while the operation is in progress. This is why calling asynchronous code from synchronous code literally doesn't even make sense. In fact, as I describe on my blog, a naive approach (using Result/Wait) can easily result in deadlocks.

The first thing to consider is: should my API be synchronous or asynchronous? If it deals with I/O (as in this example), it should be asynchronous. So, this would be a more appropriate design:

public async Task<string> RetrieveHolidayDatesFromSourceAsync() {
var result = await this.DoRetrieveHolidayDatesFromSourceAsync();
/** Do stuff **/
var returnedResult = this.TransformResults(result); /** Where result gets used **/
return returnedResult;
}

As I describe in my async best practices article, you should go "async all the way". If you don't, you won't get any benefit out of async anyway, so why bother?

But let's say that you're interested in eventually going async, but right now you can't change everything, you just want to change part of your app. That's a pretty common situation.

In that case, the proper approach is to expose both synchronous and asynchronous APIs. Eventually, after all the other code is upgraded, the synchronous APIs can be removed. I explore a variety of options for this kind of scenario in my article on brownfield async development; my personal favorite is the "bool parameter hack", which would look like this:

public string RetrieveHolidayDatesFromSource() {
return this.DoRetrieveHolidayDatesFromSourceAsync(sync: true).GetAwaiter().GetResult();
}

public Task<string> RetrieveHolidayDatesFromSourceAsync() {
return this.DoRetrieveHolidayDatesFromSourceAsync(sync: false);
}

private async Task<string> DoRetrieveHolidayDatesFromSourceAsync(bool sync) {
var result = await this.GetHolidayDatesAsync(sync);
/** Do stuff **/
var returnedResult = this.TransformResults(result);
return returnedResult;
}

private async Task<string> GetHolidayDatesAsync(bool sync) {
using (var client = new WebClient()) {
return sync
? client.DownloadString(SourceURI)
: await client.DownloadStringTaskAsync(SourceURI);
}
}

This approach avoids code duplication and also avoids any deadlock or reentrancy problems common with other "sync-over-async" antipattern solutions.

Note that I would still treat the resulting code as an "intermediate step" on the path to a properly-asynchronous API. In particular, the inner code had to fall back on WebClient (which supports both sync and async) instead of the preferred HttpClient (which only supports async). Once all the calling code is changed to use RetrieveHolidayDatesFromSourceAsync and not RetrieveHolidayDatesFromSource, then I'd revisit this and remove all the tech debt, changing it to use HttpClient and be async-only.



Related Topics



Leave a reply



Submit