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

The best answers to the question “How to call asynchronous method from synchronous method in C#?” in the category Dev.

QUESTION:

I have a public async void Foo() method that I want to call from synchronous method. So far all I have seen from MSDN documentation is calling async methods via async methods, but my whole program is not built with async methods.

Is this even possible?

Here’s one example of calling these methods from an asynchronous method:
Walkthrough: Accessing the Web by Using Async and Await (C# and Visual Basic)

Now I’m looking into calling these async methods from sync methods.

ANSWER:

Adding a solution that finally solved my problem, hopefully saves somebody’s time.

Firstly read a couple articles of Stephen Cleary:

  • Async and Await
  • Don’t Block on Async Code

From the “two best practices” in “Don’t Block on Async Code”, the first one didn’t work for me and the second one wasn’t applicable (basically if I can use await, I do!).

So here is my workaround: wrap the call inside a Task.Run<>(async () => await FunctionAsync()); and hopefully no deadlock anymore.

Here is my code:

public class LogReader
{
    ILogger _logger;

    public LogReader(ILogger logger)
    {
        _logger = logger;
    }

    public LogEntity GetLog()
    {
        Task<LogEntity> task = Task.Run<LogEntity>(async () => await GetLogAsync());
        return task.Result;
    }

    public async Task<LogEntity> GetLogAsync()
    {
        var result = await _logger.GetAsync();
        // more code here...
        return result as LogEntity;
    }
}

ANSWER:

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.

ANSWER:

async Main is now part of C# 7.2 and can be enabled in the projects advanced build settings.

For C# < 7.2, the correct way is:

static void Main(string[] args)
{
   MainAsync().GetAwaiter().GetResult();
}


static async Task MainAsync()
{
   /*await stuff here*/
}

You’ll see this used in a lot of Microsoft documentation, for example:
https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-dotnet-how-to-use-topics-subscriptions

ANSWER:

Microsoft built an AsyncHelper (internal) class to run Async as Sync. The source looks like:

internal static class AsyncHelper
{
    private static readonly TaskFactory _myTaskFactory = new 
      TaskFactory(CancellationToken.None, 
                  TaskCreationOptions.None, 
                  TaskContinuationOptions.None, 
                  TaskScheduler.Default);

    public static TResult RunSync<TResult>(Func<Task<TResult>> func)
    {
        return AsyncHelper._myTaskFactory
          .StartNew<Task<TResult>>(func)
          .Unwrap<TResult>()
          .GetAwaiter()
          .GetResult();
    }

    public static void RunSync(Func<Task> func)
    {
        AsyncHelper._myTaskFactory
          .StartNew<Task>(func)
          .Unwrap()
          .GetAwaiter()
          .GetResult();
    }
}

The Microsoft.AspNet.Identity base classes only have Async methods and in order to call them as Sync there are classes with extension methods that look like (example usage):

public static TUser FindById<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
    if (manager == null)
    {
        throw new ArgumentNullException("manager");
    }
    return AsyncHelper.RunSync<TUser>(() => manager.FindByIdAsync(userId));
}

public static bool IsInRole<TUser, TKey>(this UserManager<TUser, TKey> manager, TKey userId, string role) where TUser : class, IUser<TKey> where TKey : IEquatable<TKey>
{
    if (manager == null)
    {
        throw new ArgumentNullException("manager");
    }
    return AsyncHelper.RunSync<bool>(() => manager.IsInRoleAsync(userId, role));
}

For those concerned about the licensing terms of code, here is a link to very similar code (just adds support for culture on the thread) that has comments to indicate that it is MIT Licensed by Microsoft. https://github.com/aspnet/AspNetIdentity/blob/master/src/Microsoft.AspNet.Identity.Core/AsyncHelper.cs

Wouldn’t this be the same as just calling Task.Run(async ()=> await AsyncFunc()).Result? AFAIK, Microsoft is now discouraging from calling TaskFactory.StartNew, since they are both equivalent and one is more readable than the other.

Absolutely not.

The easy answer is that

.Unwrap().GetAwaiter().GetResult() != .Result

First off the

Is Task.Result the same as .GetAwaiter.GetResult()?

Secondly .Unwrap() causes the setup of the Task not to block the wrapped task.

Which should lead anyone to ask

Wouldn’t this be the same as just calling Task.Run(async ()=> await AsyncFunc()).GetAwaiter().GetResult()

Which would then be a It Depends.

Regarding usage of Task.Start() , Task.Run() and Task.Factory.StartNew()

Excerpt:

Task.Run uses TaskCreationOptions.DenyChildAttach which means that children’s tasks can not be attached to the parent and it uses TaskScheduler.Default which means that the one that runs tasks on Thread Pool will always be used to run tasks.

Task.Factory.StartNew uses TaskScheduler.Current which means scheduler of the current thread, it might be TaskScheduler.Default but not always.

Additional Reading:

Specifying a synchronization context

ASP.NET Core SynchronizationContext

For extra safety, wouldn’t it be better to call it like this AsyncHelper.RunSync(async () => await AsyncMethod().ConfigureAwait(false)); This way we’re telling the “inner” method “please don’t try to sync to upper context and dealock”

Really great point and as most object architectural questions go it depends.

As an extension method do you want to force that for absolutely every call, or do you let the programmer using the function configure that on their own async calls? I could see a use case for call three scenarios; it most likely is not something you want in WPF, certainly makes sense in most cases, but considering there is no Context in ASP.Net Core if you could guarantee it was say internal for a ASP.Net Core, then it wouldn’t matter.