# Running an async function synchronously

Sometimes I have the need to call an async function from a non-async function and get the result back synchronously. Therefore I wrote the following helper function to be able to do this in one line.

public static T RunSync<T>(Func<Task<T>> taskConstructor)
{
var signal = new ManualResetEventSlim();
T result;
Exception ex = null;
ThreadPool.QueueUserWorkItem(async () =>
{
try
{
var task = taskConstructor();
result = await task.ConfigureAwait(false);
}
catch (Exception iex)
{
ex = iex;
}
finally
{
signal.Set();
}

});
signal.Wait();
if (ex != null)
throw ex;
return result;
}


Is this ok to do or will it behave unexpectedly in some edge cases? I know that writing this kind of async functions in can be tricky and can easily deadlock. That's why I ran it inside a ThreadPool thread which should prevent any kind of deadlock.

I think that this is probably not the most performant way to do it, but preventing deadlocks is more important in my case.

I know that re-throwing an exception is not ideal, but I think this is a compromise I have to make here.

• Does it have any benefit over just doing task.ConfigureAwait(false) .GetAwaiter() .GetResult(); – Anders Apr 25 '20 at 9:17
• @Anders Good question actually. I think it does. I think your version may deadlock if the task does not use ´ConfigureAwait(false)´ internally. – LittleEwok Apr 26 '20 at 19:45
• This article shows why code such at this is a bad idea in general. – Alejandro Apr 27 '20 at 15:07

## 1 Answer

public static class TaskExtensions
{
private static readonly TaskFactory TaskFactory = new TaskFactory(
CancellationToken.None,
TaskCreationOptions.None,
TaskContinuationOptions.None,
TaskScheduler.Default);

public static TResult RunSync<TResult>(this Func<Task<TResult>> asyncFunc)
=> TaskFactory
.StartNew(asyncFunc)
.Unwrap()
.GetAwaiter()
.GetResult();

public static void RunSync(this Func<Task> asyncAction)
=> TaskFactory
.StartNew(asyncAction)
.Unwrap()
.GetAwaiter()
.GetResult();
}


There are several things that worth mentioning:

• Async methods were designed to be used all the way. So, if you call an async I/O operation in the bottom layer then it should be be called in an async fashion till the top layer.
• Async operations are sensitive for Exceptions. They can behave differently based on how you call them. (They can be swallowed, thrown as an AggregateException or thrown normally. Here the UnWrap + GetAwaiter do the magic for us to be able to handle the exception normally.
• In order to avoid deadlocks the async operation is passed to an other Task, that is where TaskFactory.StartNew comes into play.