MVC4 WebApi进程启动器
我有一个MVC4 API REST试图在新线程中启动一个进程。我正在使用框架4.5并尝试使用同步并等待clausules。MVC4 WebApi进程启动器
我的代码如下所示:
[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;
}
}
理想的情况下,有人将执行API REST调用(/任务/ slowtask /发射)与POST动词和期望202(接受)非常快。
使用Fiddler Web调试器我发出请求,代码进入Launch(// 1),然后进入await(// 3),慢速任务被创建并启动并返回Accepted(// 2)。但在这一点上,提琴手不显示202的结果。见所附图像:
http://imageshack.us/photo/my-images/703/fiddler1.png/
当慢任务结束,代码继续捕获退出码(// 4),然后将202被捕获到的Fiddler。
这很奇怪,因为很久以前我做了回报。我错过了什么?我怎样才能改变这个代码,以便快速返回一个202并忘记这个任务。
注意:我知道如何做到这一点与非框架4.5功能,我想学习如何使用async/await。
我认为这是因为runProcessAsync()
在请求的同步上下文上运行。如果你不想要,你可以使用Task.Run(() => runProcessAsync(id));
。但要小心,因为在那段时间IIS可以回收你的AppDomain,所以有机会runProcessAsync()
不会完成。
最简单的方法是将runProcessAsync
更改为返回Task
。请记住,async
方法应尽可能返回Task
/Task<T>
,并且只有当它们的有时才返回void
。
但是,我必须告诫你,这是相当危险的。 ASP.NET是一个HTTP服务器,所以如果没有活动的请求,它会随时取消你的AppDomain,如果它想的话。这意味着你的“用exitcode做更多的事情”将会从地球上消失。
我有一个blog post with a BackgroundTaskManager
,您可以使用它在ASP.NET运行时注册Task
。如果有尚未完成的注册Task
s,它将尝试延迟AppDomain关闭。但是,这只是一种“尽最大努力”。这种事情没有保证。在ASP.NET中运行的任何代码都与活动请求无关,这是一种危险的情况。
我不明白,为什么要改变返回类型的'任务'帮助?如果问题出现在同步上下文中,那么这不会改变什么,不是吗? – svick 2013-02-15 23:08:13
我认为这是因为'runProcessAsync()'运行在请求的同步上下文中。如果你不想这样做,你可以使用'Task.Run(()=> runProcessAsync(id));'。但要小心,因为IIS可以在此期间回收您的appdomain,所以有可能'runProcessAsync()'不会完成。 – svick 2013-02-15 15:18:01
我做到了,工作。这是一个遗憾,你没有给出正确的答案,因为你解决了我的问题 – Jordi 2013-02-18 09:02:57