MatthewMartin
MatthewMartin

Reputation: 33173

How do I spawn a task that will run to completion and immediately return to the client?

So I'm trying to create a proof of concept for server-side asynch using HTTP 202 codes (where the server accepts the task, immediately returns an endpoint to poll and then creates/updates the resource)

Rick Strahl has a description of how to do this in ordinary ASP.NET. That technique depends on being able to Response.End and then continue to execute code. The Response object doesn't even seem to be available in a Web API controller's context.

If the following worked as planned, it would not block returning the http 202 and still guarantee that the database task will run to completion.

//Insert or Update Asych. 
public Task<HttpResponseMessage> Post(bool asynch, [FromBody]DatedValue value) //Insert Value
{
    Guid key = Guid.NewGuid();

    //Want this to run to completion, even if response associated with parent thread is done.
    Task toWait = Task.Factory.StartNew(() =>
    {
        queue.Add(key, 0);
        DatedValue justCreated = Insert(value);
        queue[key] = justCreated.Id;
    });

    //Return address to resource just created.
    Task<HttpResponseMessage> sender = Task.Factory.StartNew(() =>
    {
        HttpResponseMessage message = Request.CreateResponse(HttpStatusCode.Accepted);
        message.Headers.Location = new Uri("/ValueQueue/" + key);
        return message;
    });

    Task.WaitAll((new[] { toWait, sender }));

    return sender;
}

Upvotes: 1

Views: 927

Answers (1)

Task.WaitAll blocks execution and response is not returned until both the tasks are completed. If you change your code something like below, you should be able to return the response while the task is being run.

public HttpResponseMessage Post(bool asynch, [FromBody]DatedValue value)
{
    Guid key = Guid.NewGuid();

    Task.Factory.StartNew(() =>
    {
        queue.Add(key, 0);
        DatedValue justCreated = Insert(value);
        queue[key] = justCreated.Id;
    });

    HttpResponseMessage message = Request.CreateResponse(HttpStatusCode.Accepted);
    message.Headers.Location = new Uri("/ValueQueue/" + key);
    return message;
}

However, you should be aware of a problem with an approach like this. If you are hosting Web API on IIS, your worker process can get recycled while your task is running. Since you have already returned the response, as far as ASP.NET is concerned, it has completed its work. So, if for some reason IIS decides to recycle the worker process, it will go ahead regardless of where your task is in terms of execution and hence you can end up with corrupt data.

With .NET 4.5.2, you can use QueueBackgroundWorkItem. Read this - http://blogs.msdn.com/b/webdev/archive/2014/06/04/queuebackgroundworkitem-to-reliably-schedule-and-run-long-background-process-in-asp-net.aspx.

Upvotes: 3

Related Questions