我有一个 MVC4 API REST 尝试在新线程中启动进程。我正在使用框架4.5并尝试使用sync和等待clausules。
我的代码如下:
[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;
}
}
理想情况下,有人会使用POST动词执行api rest调用(/ task / slowtask / launch),并期望202(接受)非常快。
使用Fiddler Web调试器我发出请求,代码进入Launch(// 1),然后进入await(// 3),创建并启动慢速任务并返回Accepted(// 2 )。但在这一点上,Fiddler没有显示202结果。见附图:
http://imageshack.us/photo/my-images/703/fiddler1.png/
当慢速任务结束时,代码继续捕获exitcode(// 4),然后202被捕获到Fiddler中。
这很奇怪,因为很久以前我做过回归。我错过了什么?如何更改代码以便快速返回202并忘记任务。
注意:我知道如何使用非框架4.5功能,我正在尝试学习如何使用async / await。
答案 0 :(得分:4)
我认为这是因为runProcessAsync()
在请求的同步上下文上运行。如果您不想这样,可以使用Task.Run(() => runProcessAsync(id));
。但要小心,因为IIS可以在此期间回收您的AppDomain,因此runProcessAsync()
可能无法完成。
答案 1 :(得分:1)
最简单的方法是更改runProcessAsync
以返回Task
。请注意,async
方法应尽可能返回Task
/ Task<T>
,并且只有当 时才返回void
。
但是,我必须提醒你,这是非常危险的。 ASP.NET是一个HTTP服务器,因此如果没有活动请求,它可以随意删除您的AppDomain。这意味着你“用exitcode做更多东西”只会从地球上掉下来。
我有一个blog post with a BackgroundTaskManager
可用于向ASP.NET运行时注册Task
。如果已注册的Task
尚未完成,它将尝试延迟AppDomain关闭。然而,这只是“尽力而为”;这种事情无法保证。在ASP.NET中运行的任何与活动请求无关的代码都是危险的情况。