MVC4 WebApi process launcher

假装没事ソ 提交于 2019-12-21 04:30:13

问题


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.


回答1:


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.




回答2:


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 Tasks with the ASP.NET runtime. It will attempt to delay AppDomain shutdowns if there are registered Tasks 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.



来源:https://stackoverflow.com/questions/14896057/mvc4-webapi-process-launcher

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!