Home > OS >  Sync over Async generic methods. How ExecSync call ExecAsync?
Sync over Async generic methods. How ExecSync call ExecAsync?

Time:08-18

You work with latest and greatest lucky you!!. Some of us have no options but support legacy code that we cannot modify etc.. We cannot do async all the way....

So sync over async in an old asp.net mvc4 web application and following this article Brownfield Async Development and a previous post of mine

It suggests doing something like below to avoid deadlock.

public SomeStuff GetSomestuff(Whatever whatever)
{
    return GetSomestuffAsync(whatever).GetAwaiter().GetResult();
}

public async Task<SomeStuff> GetSomestuffAsync(Whatever whatever)
{
    // Do NOT queue a synchronization context continuation.
    return myRefitHttpClient.GetSomestuff(whatever).ConfigureAwait(false);
}

My question:

I want to make 2 generic methods - ExecAsync and ExecSync so that I drastically reduce the code I need to write. I have the the following (removed logging etc.. )

    public async Task ExecAsync<T>(
        Func<Task<T>> method,
        Action<T> response)
    {
        T result = await method.Invoke().ConfigureAwait(false);
        response(result);
    }

But now I want the "Sync version calling the async one" but cannot get it compile/working

    public void ExecSync<T>(
        Func<Task<T>> method,
        Action<T> response)
    {
         //BELOW IS WRONG! I need to call Async and then GetAwaiter().GetResult()
        //ExecAsync(()=>method, r => response = r).GetAwaiter().GetResult();
        //response(r);
    }

Any suggestions?

UPDATE

Find this code below : is below a decent solution for sync over async

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 void RunSync(Func<Task> task)
    {
        //Storing the context
        var storeContext = HttpContext.Current;

        taskFactory
            .StartNew(task)
            .Unwrap()
            .GetAwaiter()
            .GetResult();
       //restoring context
        HttpContext.Current = storeContext;
    }


    public static TResult RunSync<TResult>(Func<Task<TResult>> task)
    {
        var storeContext = HttpContext.Current;

        var result= taskFactory
            .StartNew(task)
            .Unwrap()
            .GetAwaiter()
            .GetResult(); 
        //restoring context
        HttpContext.Current = storeContext;

        return result;
    }
}

CodePudding user response:

cannot get it compile/working

Getting it to compile is easy enough:

public void ExecSync<T>(Func<Task<T>> method, Action<T> response)
{
  ExecAsync(method, response).GetAwaiter().GetResult();
}

However, getting it working is more of a challenge.

You are asking for a generic, general-purpose sync-over-async solution. This does not exist. If it did, then articles like Brownfield Async wouldn't have been written; everyone could just use the generic solution and be done with it.

The current solution assumes that your code uses ConfigureAwait(false) for every await in your app. If it does, then fine.

Another solution is the thread pool hack, but that one assumes that the code can run on any arbitrary thread pool thread. On old ASP.NET code, this may not be the case; in particular code that accesses HttpContext.Current.

  • Related