【问题标题】:Run method on a separate thread inside Action在 Action 内的单独线程上运行方法
【发布时间】:2015-09-06 05:35:26
【问题描述】:

假设我有一个像下面这样的操作,我想尽快返回视图并继续在后台线程中做一些工作。

public async Task<ActionResult> Index()
{
    Debug.WriteLine("Inside Index");

    var newCustomer = new Customer
    {
        Name = "Ibrahim"
    };

    Task.Run(() => SaveCustomer(newCustomer));

    Debug.WriteLine("Exiting Index");

    return View();
}

private async Task SaveCustomer(Customer NewCustomer)
{
    Debug.WriteLine("Started Saving Customer");

    await Task.Delay(2000);

    Debug.WriteLine("Completed Saving Customer");
}

我确实得到了预期的输出,即:

Inside Index
Exiting Index
Started Saving Customer
Completed Saving Customer

但令我困扰的是,我收到警告说我的Index 操作将同步运行,我应该放置一个await,但是在SaveCustomer 完成后返回视图并且目的被打败了。

我怎么做错了?任何建议表示赞赏。

【问题讨论】:

  • 可能有点跑题了——但是如果SaveCustomer(newCustomer) 失败了怎么办?当前的请求不应该意识到这一点吗?
  • @shay__ 是的,当然。但这只是一个例子。我只是想知道我想做的是否正确。
  • 例如可以作为日志机制而不是保存客户

标签: asp.net-mvc multithreading asynchronous async-await task-parallel-library


【解决方案1】:

但困扰我的是,我收到一条警告说我的索引操作将同步运行

我怎么做错了?

不要自上而下强制异步。相反,从自然异步操作开始在最低级别(例如,EF6 数据库访问),并允许异步从最低级别的代码向上增长。

另外,在 ASP.NET 上,您应该强烈避免使用Task.Run

应用这两个原则会产生一个像这样的Index 方法:

public async Task<ActionResult> Index()
{
  Debug.WriteLine("Inside Index");

  var newCustomer = new Customer
  {
    Name = "Ibrahim"
  };

  await SaveCustomer(newCustomer);

  Debug.WriteLine("Exiting Index");

  return View();
}

但是在 SaveCustomer 完成后返回视图并且目的被破坏。

一点也不。异步 ASP.NET 代码的目的是不是提前返回给客户端。 asyncawait 不会更改 HTTP 协议。服务器端的await 屈服于线程池,而不是客户端。

如果您需要早点返回(而大多数人不需要 - 他们只是认为他们“需要”这样做),那么您应该使用one of the established patterns for returning early(如我在我的博客上描述)。请注意,唯一正确(即完全可靠)的解决方案需要设置一个具有独立后台进程的可靠队列。

【讨论】:

  • OP - 你应该一遍又一遍地阅读这个答案,直到你真正明白......它都在那里。
【解决方案2】:

您的Index 根本没有使用任何异步功能。为什么将其标记为async?你一定是误会了什么,不知道是什么。删除async Task 规范。

【讨论】:

  • 误解是我在某处读到,用于 HTTP 处理的保留线程有限,所以如果操作不是异步的并且被阻塞,那么最终将没有可用的线程,这就是为什么我将 Index 标记为异步开始。这是错的吗?也许在这里不适用?
  • 将其标记为异步不会释放线程。如果是这种情况,所有方法都将始终是异步的。 async 几乎只是启用 await 关键字。听着,您有 99% 的机会不需要异步 IO(在 ASP.NET 下)。几十年来,我们主要在没有异步 IO 的情况下运行我们的服务器。在使用它之前,请确保您了解它的用途和原因。
  • 我一般会给出以下链接:stackoverflow.com/a/25087273/122718 为什么EF 6教程使用异步调用? stackoverflow.com/a/12796711/122718我们应该切换到默认使用异步 I/O 吗?
  • 我同意@usr,很可能你不需要异步后端。在我的公司中,我们将代码重构为仅当我们变得足够大时才异步,“太多”客户和每秒 30K+ 的请求。
【解决方案3】:

您会收到编译器警告,因为您的 Index() 方法中没有任何异步内容。您的 Task.Run(() =&gt; SaveCustomer(newCustomer)); 行意味着 Fire And Forget(非等待任务) - 这与 异步 代码非常不同。 Index() 是完全同步的,同时创建一个“辅助任务”以在将来的某个时间执行。正如提到的另一个答案 - 您也可以从您的方法中删除异步标记 - 它不是异步的。

【讨论】:

  • 所以我的Index 操作实际上并没有阻塞并且不需要async
  • @lbrahim 正确,您的 示例 中没有任何阻塞。但话又说回来,这只是一个例子,不是吗?你的真实代码不会进行任何 I/O 调用吗?
猜你喜欢
  • 1970-01-01
  • 2015-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-22
相关资源
最近更新 更多