1

我有一个 ASP.NET MVC 异步操作方法,如下所示:

public async Task<ActionResult> IndexAsync()
{
    var tasks = new List<Task<string>>
    {
        GetSomethingAsync("a"),
        GetSomethingAsync("b")
    };

    await Task.WhenAll(tasks);

    return View();
}

private async Task<string> GetSomethingAsync()
{
    var data = await _someService.GetSomethingAsync().ConfigureAwait(false);
    return data.SomeData;
}

现在,当我调试并跳过tasks变量创建时,任务会立即执行。换句话说,当我将鼠标悬停tasksawait队列中时,他们会说“RanToCompletion”。

为什么?

据我了解,应该创建任务,但在被await Task.WhenAll(tasks)阻塞调用触发之前处于“WaitingForActivation”状态。

有人可以向我解释发生了什么吗?我以前写过这样的代码,它通常按预期工作,所以我想知道这是 ASP.NET 还是 ASP.NET MVC 异步控制器?

TIA。

编辑 如果我将代码更改为:

var tasks = new List<Task<string>>
{
   Task.Run(() => GetSomethingAsync("a")),
   Task.Run(() => GetSomethingAsync("b"))
};

该方法按预期运行(任务直到 才执行await)。

在运行异步任务之前,我通常不需要这样做,这在 ASP.NET MVC 中是否需要?

4

2 回答 2

3

根据您的评论,您实际上没有任何真正的异步代码-因此确实任务将同步返回并处于完成状态。

使方法真正异步的最简单方法是await Task.Yield(). 这对于单元测试或由于某种原因必须异步但不会消耗太多时间的方法来说很好。如果您需要运行慢速(阻塞或只是 CPU 密集型)方法 -Task.Run正如您在问题中所遇到的,这是使任务在单独线程上运行的合理方法。

笔记

  • 标记方法async本身不会使其异步,也不会自行await创建任何线程。
  • 网络调用最好使用真正的异步方法。ASP.Net 的线程拉取有限,阻塞调用的消耗线程将耗尽负载下的拉取,导致死锁,因为await'ing 方法将无法找到要运行的线程。
  • usingConfigureAwait(false)不能防止 ASP.Net 中基于负载的死锁,并且可以方便地松动HttpContext.Current和线程CultureInfo- 在 ASP.Net 中使用它时要小心,尤其是这样做基本上没有好处(与新线程相比,在新线程上恢复上下文的成本非常低WPF/WinForm 跨线程调用)。
于 2017-04-06T01:52:00.127 回答
2

现在,当我调试并跳过任务变量创建时,任务会立即执行。换句话说,当我将鼠标悬停在等待行中的任务上时,他们会说“RanToCompletion”。

我建议你阅读我的async介绍。引用:

异步方法的开头就像任何其他方法一样执行。也就是说,它会同步运行,直到遇到“等待”(或抛出异常)。

因此,如果您有返回已完成任务的存根异步方法,那么您的方法调用(例如,GetSomethingAsync("a"))将同步完成。该任务在添加到列表时已经完成。

据我了解,应该创建任务,但处于“WaitingForActivation”状态,直到由 await Task.WhenAll(tasks) 阻塞调用触发。

WaitingForActivation是一个不幸的名字。对于 Promise 任务,WaitingForActivation状态意味着它实际上已经在进行中

没有需要通过Task.WhenAllor发生的“触发” awaitTask.Run(() => GetSomethingAsync("a"))所创建的任务已经在进行中。

这可以在调试器中通过在await Task.Delay(1000);之前插入一个来观察await Task.WhenAll,并在该延迟之后检查任务的状态。

这在 ASP.NET MVC 中需要吗?

不。事实上,你应该避免Task.Run在 ASP.NET 上。引用我关于异步 ASP.NET 的 MSDN 文章:

您可以通过等待 Task.Run 来开始一些后台工作,但这样做没有任何意义。事实上,这实际上会干扰 ASP.NET 线程池试探法,从而损害您的可伸缩性。如果您在 ASP.NET 上有 CPU 密集型工作要做,最好的办法是直接在请求线程上执行它。作为一般规则,不要将工作排队到 ASP.NET 上的线程池中。

于 2017-04-06T12:49:46.310 回答