7

我有一个MVC4 API REST试图在新线程中启动一个进程。我正在使用框架 4.5 并尝试使用同步和等待子句。

我的代码如下所示:

[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 休息调用 ( /task/slowtask/launch ) 并期望 202 (accepted) 非常快。

我使用 Fiddler Web Debugger 发出请求,代码进入 Launch (// 1),然后进入 await (// 3),创建并启动慢速任务并返回 Accepted (// 2)。但是此时,Fiddler 并没有显示 202 结果。见附图:

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

当慢任务结束时,代码继续捕获exitcode(// 4),然后将202捕获到Fiddler中。

这很奇怪,因为我很久以前就返回了。我错过了什么?我如何更改该代码以非常快速地返回 202 而忘记任务。

注意:我知道如何使用非框架 4.5 功能来做到这一点,我正在尝试学习如何使用异步/等待。

4

2 回答 2

4

我认为这是因为runProcessAsync()在请求的同步上下文上运行。如果你不想这样,你可以使用Task.Run(() => runProcessAsync(id));. 但要小心这一点,因为 IIS 可以在那段时间回收你的 AppDomain,所以有可能runProcessAsync()无法完成。

于 2013-02-18T09:07:41.873 回答
1

最简单的方法是更改runProcessAsync​​为返回一个Task. 请记住,async方法应尽可能返回Task/ Task<T>,并且仅在必要void时返回。

但是,我必须提醒您,这是非常危险的。ASP.NET 是一个 HTTP 服务器,因此如果没有活动请求,它可以随意关闭您的 AppDomain。这将意味着您的“使用退出代码做更多的事情”只会从地球上掉下来。

我有一篇博客文章BackgroundTaskManager,您可以使用它来将Tasks 注册到 ASP.NET 运行时。Task如果有已注册的尚未完成,它将尝试延迟 AppDomain 关闭。但是,这只是“尽力而为”;这种事情没有任何保证。在 ASP.NET 中运行的任何与活动请求无关的代码都是危险的情况。

于 2013-02-15T20:31:09.383 回答