2

Let's suppose i have the following async method that needs fairly long time until completing its work:

void async Task LongWork()
{
    await LONGWORK() // ... long work 
}

Now, in an web api, i would like to run that work in a background (i.e., i want to return the Http Request after starting that LongWork() but before its completion:

I can think of three approaches to achieve this:

1) public async Task<string> WebApi()
   {
       ... // do another work

       await Task.Factory.StartNew(() => LongWork());

       return "ok";
   }
2) public async Task<string> WebApi()
   {
       ... // do another work

       await Task.Factory.StartNew(async () => await LongWork());

       return "ok";
   }

3) public async Task<string> WebApi()
   {
       ... // do another work

       Task.Factory.StartNew(async () => await LongWork());

       return "ok";
   }

Q1: What's the difference between approach #1 and #2?

Q2: What is the right way to, in the ASP.NET world, run a method (in this example, LongWork() containing some async/await pairs in a background thread? In particular, in #3, there's no "await" before Task.Factory.StartNew(async () => await LongWork()). Is it fine?

Thanks!

soleiljy
  • 1,121
  • 2
  • 13
  • 20

1 Answers1

10

Q1: What's the difference between approach #1 and #2?

#1 has less overhead. That is the only difference.

Q2: What is the right way to, in the ASP.NET world, run a method (in this example, LongWork() containing some async/await pairs in a background thread?

None of the options you provided. For one thing, they all use Task.Factory.StartNew without specifying a TaskScheduler, which is dangerous (as I describe on my blog). They should use Task.Run instead. However, even if you use Task.Run, you will run into a more serious underlying problem.

The underlying problem is this: the HTTP protocol is centered around each request having exactly one matching response. When an HTTP server (such as ASP.NET) knows that there are no outstanding requests, it will make assumptions like "it's safe to recycle the worker process".

I describe this problem in more detail on my blog. Also in that blog post is a type BackgroundTaskManager that registers background tasks with the ASP.NET runtime and (correctly) executes them via Task.Run. You should only use BackgroundTaskManager if you read the blog post and understand and accept that this is still dangerous and unsafe.

A far better (read: more reliable) solution is to first write out a representation of the work to be done to persistent storage (e.g., an Azure queue) and have an independent backend process (e.g., an Azure worker role) that processes requests from the queue.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810