Pregunta

I have an HTTPHandler in asp.net that I would like to convert to be async using 4.5 async await code. However not all of our code is 4.5 so the inner parts of our app (libraries, infrastructure) needs to stay on 4.0 at the moment. I'm running into an issue where I want to provide a lambda to some of our shared libraries. The lambda is async but I need the library code to wait on it to complete, but without deadlocking. So far I haven't been able to figure it out.

Let's say the handler looks like this:

// .NET 4.5 website
public class MyHandler : HttpTaskAsyncHandler
{
    public override async Task ProcessRequestAsync(HttpContext context)
    {
         var someLibrary = new SharedLibrary();
         someLibrary.PostProcess = async (x,y) => {
             await ProcessXY(x,y);
         }

         await someLibrary.ProcessAsync();
         // write out some content here
    }

    private async Task ProcessXY(int x, int y) {
        // make some external calls
        var someLib = new SomeOtherLibrary();
        await someLib.CallAsync(x,y);
    }
}

And then SharedLibrary looks something like this:

// .NET 4.0 class library
public class SharedLibrary {
    public Action<int,int> PostProcess { get; set; }

    public Task ProcessAsync() {
        return Task.Factory.StartNew(() => Process(), CancellationToken.None,TaskCreationOptions.None,TaskScheduler.FromCurrentSynchronizationContext());
    }

    public void Process() {
       // do some processing
       if (PostProcess != null) {
           // call PostProcess synchronously
           PostProcess(1,2);
       }
       // do some final processing
    }
}

As the code stands now, the call to PostProcess returns before the lambda fully finishes running. If I switch PostProcess to Func<int,int,Task> instead and call as PostProcess(1,2).Wait() then I deadlock. Is there a way to accomplish in mixed 4.5/4.0 code like this?

¿Fue útil?

Solución

Use ConfigureAwait(false) inside your lambda:

public override async Task ProcessRequestAsync(HttpContext context)
{
     var someLibrary = new SharedLibrary();
     someLibrary.PostProcess = async (x,y) => {
         await ProcessXY(x,y).ConfigureAwait(false);
     }

     await someLibrary.ProcessAsync();
     // write out some content here
}

public class SharedLibrary {
  public Func<int,int,Task> PostProcess { get; set; }

  public void Process() {
   // do some processing
    if (PostProcess != null) {
       // call PostProcess synchronously
       PostProcess(1,2).Wait();
    }
   // do some final processing
  }
}

This way you should avoid deadlocking on the UI thread.

Edit

As mentioned on the comments, once ConfigureAwait(false) is used inside ASP.NET, The HttpContext is no longer avaliable inside the continuation (code after await)

I would suggest you look into the Microsoft.Bcl.Async library deployed for .NET 4 via Nuget, so you could use async/await properly without initializing Threads for no good reason:

http://blogs.msdn.com/b/bclteam/p/asynctargetingpackkb.aspx

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top