简体   繁体   中英

MVC4 WebApi process launcher

I have a MVC4 API REST trying to launch a process in a new thread. I am working with framework 4.5 and trying to use sync and await clausules.

My code looks like:

[AcceptVerbs("POST")]
public HttpResponseMessage Launch(string id)
{
    runProcessAsync(id);   // 1                                                        

    return Request.CreateResponse(HttpStatusCode.Accepted); // 2
}

protected async void runProcessAsync(string id)
{
    int exitcode = await runProcess(id);  // 3
    string exitcodesz = string.Format("exitcode: {0}", exitcode);  // 4
    // do more stuff with exitcode
}

protected Task<int> runProcess(string id)
{
    var tcs = new TaskCompletionSource<int>();

    var process = new Process
    {
        StartInfo = { FileName = @"C:\a_very_slow_task.bat" },
            EnableRaisingEvents = true
        };
        process.Exited += (sender, args) => tcs.SetResult(((Process)sender).ExitCode);            

        process.Start();

        return tcs.Task;
    }  
}

Ideally, someone will perform a api rest call ( /task/slowtask/launch ) with POST verb and expect a 202 (accepted) very fast.

Using Fiddler Web Debugger I make the request, the code enters into Launch (// 1), then goes to the await (// 3), the slow task is created and started and a Accepted is returned (// 2). But at this point, Fiddler doesn't show the 202 result. See attached image:

http://imageshack.us/photo/my-images/703/fiddler1.png/

When the slow task ends, the code continues capturing the exitcode (// 4) and then the 202 is captured into Fiddler.

That is very weird, because I did the return long time ago. What I am missing? how can I change that code in order to return a 202 very fast and forget about the task.

Note : I know how to do this with non framework 4.5 features, I am trying to learn how to use async / await.

I think this is because runProcessAsync() runs on the request's synchronization context. If you don't want that, you can use Task.Run(() => runProcessAsync(id)); . But be careful with this, because IIS can recycle your AppDomain during that time, so there is a chance runProcessAsync() won't complete.

The easiest way is to just change runProcessAsync to return a Task . Remember, async methods should return Task / Task<T> whenever possible, and only return void when they have to.

However, I must caution you that this is quite dangerous. ASP.NET is an HTTP server, so if there are no active requests, it will feel free to take down your AppDomain if it wants to. This would mean your "do more stuff with exitcode" will just drop off the face of the earth.

I have a blog post with a BackgroundTaskManager that you can use to register Task s with the ASP.NET runtime. It will attempt to delay AppDomain shutdowns if there are registered Task s that have not completed. However, it's just a "best effort"; there are no guarantees for that kind of thing. Any code running in ASP.NET that is not associated with an active request is a dangerous situation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM