16

在我的 MVC4 应用程序中,我需要添加一个控制器来上传和处理大文件。文件上传后,我需要立即开始对该文件进行异步处理并将响应返回给浏览器,而无需等待处理完成。

显然我可以启动一个新线程来手动处理文件,但我想知道我是否可以使用 .net 4.5 引入的 async/await 机制来实现这个场景

为了测试这个概念,我尝试了这样的事情:

public async Task<ActionResult> Test()
{
    TestAsync();
    return View("Test");
}

public async void TestAsync()
{
    await LongRunning();
}

private Task<int> LongRunning()
{
    return Task<int>.Factory.StartNew(() => Pause());
}

private int Pause()
{
    Thread.Sleep(10000);
    return 3;
}

异步机制似乎在一般情况下工作:当我调试代码时,我点击了“return View("Test");” “return 3”行之前的行。但是,浏览器仅在 Pause 方法完成后才会收到响应。

这似乎表现得像常规异步控制器(具有 Async 和 Completed 方法的控制器)。有没有办法在我的场景中使用控制器中的异步/等待?

4

3 回答 3

14

显然我可以启动一个新线程来手动处理文件,但我想知道我是否可以使用 .net 4.5 引入的 async/await 机制来实现这个场景

不,你不能,因为async不会改变 HTTP 协议

Svick 和 James 已经将正确答案作为评论发布,为方便起见,我将其复制如下:

IIS 几乎可以随时回收您的应用程序。

如果您有长时间运行的事情要与请求异步,请在其他地方执行。“典型”是一个持久队列(MSMQ、Azure、RabbitMQ 等),其他东西(Windows 服务、由任务调度程序运行的 exe、使用 Quartz.net 的应用程序等)处理它们。

总而言之,HTTP 给你一个请求和一个响应(async- 以及其他任何东西 - 不会改变这一点)。

ASP.NET 是围绕 HTTP 请求设计的(并做出诸如“如果没有未完成的请求,则停止该网站是安全的”之类的假设)。您可以启动一个新线程并将上传的内容保存在内存中(这是最简单的方法),但强烈不建议这样做。

对于您的情况,我建议您遵循 James 的建议:

  • 上传完成后,将上传保存到持久存储(例如,Azure 队列),并向浏览器返回“票证”。
  • 让其他进程(例如,Azure 辅助角色)处理队列,最终将其标记为完成。
  • 让浏览器用它的“票”轮询服务器。服务器使用“票证”在持久存储中查找文件并返回它是否完整。

这有一些变化(例如,当处理完成时使用 SignalR 通知浏览器),但一般架构是相同的。

这很复杂,但这是正确的方法。

于 2012-09-07T18:45:17.190 回答
2

您的代码中有错误。您必须在操作中等待对 TestAsync 的调用。如果你不这样做,它只会返回一个“任务”对象而不运行任何东西。

public async Task<ActionResult> Test()
{
    await TestAsync();
    return View("Test");
}

在异步方法中睡眠的正确方法是调用

await Task.Delay(10000);

您必须更改 TestAsync 的签名:如果方法返回 void,则不应将其标记为异步。它必须返回 Task 。返回 void 是为了与 .net 事件兼容而保留的。

public async Task TestAsync()
{
    await LongRunning();
}

方法体是一样的。

于 2012-09-14T14:01:50.590 回答
0

您的 LongRunning 方法正在同步休眠 10 秒。更改它,以便在任务中发生睡眠。

于 2012-09-07T14:15:03.247 回答