【问题标题】:Is there any benefit to using Task.Factory.StartNew<Action>() in this ASP.NET .ashx code snippet?在这个 ASP.NET .ashx 代码片段中使用 Task.Factory.StartNew<Action>() 有什么好处吗?
【发布时间】:2015-03-31 18:13:31
【问题描述】:

这是一个 .ashx 处理程序,它创建一系列 Task 对象,然后在 foreach 循环中执行。

        List<Task<Action>> list = new List<Task<Action>>();

        list.Add(Task.Factory.StartNew<Action>(() => AddHeader(graphics, request, reportParameters)));
        list.Add(Task.Factory.StartNew<Action>(() => AddSubmitter (graphics, request, reportParameters)));
        list.Add(Task.Factory.StartNew<Action>(() => AddActivity(graphics, request, reportParameters)));
        list.Add(Task.Factory.StartNew<Action>(() => AddCharts(graphics, request, reportParameters)));

        //Task.WaitAll(list.ToArray()); //AY - This seems unnecessary
        foreach (var action in list.Select(t => t.Result))
        {
            action();
        }

每个返回 Action 的方法都会像下面的第一个方法一样执行 CPU 密集型工作。

private Action AddHeader(XGraphics graphics, HttpRequest request, ReportParameters reportParameters)
    {
        DataSet ds = new myData().ClientHeader(reportParameters);

        // This will be a function waiting to be called.
        return () =>
        {
          // Some CPU intensive operation code here.
        };
    }

有人可以帮我理解这段代码是否有用吗 1)从异步或多任务的角度来看? 2) 这段代码是否甚至从 ASP.NET 线程池中创建了多个线程?

当我调试应用程序时,它会在运行 foreach 循环之前在我的机器上显示 4 个单独的任务。这些任务中的每一个都检索数据集,我认为这是因为每个方法中的那部分代码在 return () Action 语句之前运行。

那么这段代码的全部目的是在执行每个 CPU 密集型操作方法之前在单独的任务中启用多个数据库调用吗?或者代码真的在做异步的事情吗?

【问题讨论】:

  • 在 asp.net 中使用任务异步模式有一个好处——工作负载显然被转移到了应用程序池之外的线程上,这意味着您可以获得更多的并发性和更多的会话。不确定这与您的具体实施有何关系,因此仅发表评论。
  • @kidshaw 不,在 ASP.NET 中使用异步模式的主要好处是允许您使用更少的线程,而不是使用不同的线程。这允许线程池将更多时间花在生产性工作、处理请求上,而不是非生产性工作,比如坐等 IO。

标签: c# asp.net multithreading async-await task-parallel-library


【解决方案1】:

这不是利用代表线程池执行单元的任务的最佳方式:

  1. 因为每个这样的任务都会阻塞线程池中的一个线程,并且 ASP.NET 使用the same thread pool 作为任务,所以您可以将并发请求的数量减少五倍。
  2. 将线程池线程用于数据库 (I/O) 绑定代码 is ineffective,因为线程池线程只会处于空闲状态,因此请使用 async code 进行数据库调用。
  3. 但这不是最后一个问题 - foreach (var action in list.Select(t =&gt; t.Result)) 将阻止执行,直到它按顺序从每个任务中获取 Result。这意味着返回的操作不会在它们准备好执行后立即执行,它们将按照它们添加到列表中的顺序执行,从而否定如果我们一旦执行它们就可能获得的任何潜在优势准备好了。

所以,作为结论:

这样的代码只是以资源效率最低的方式启动 4 个数据库调用,然后浪费了 4 个 mis 使用的线程池线程,在原始线程上执行它们的“延续”,而不是利用 4 个任务可能以任意顺序执行的可能性。

P.S.: 即使您确实需要进行 CPU 密集型处理,也不要使用 Factory.StartNew - 使用 Task.Run。这不是真正的错误,因为它们基本上做同样的事情,而是a good recommendation

【讨论】:

  • 如果 Factory.StartNew 被 Task.Run 取代,它不会仍然消耗 ASP.NET 线程池中的线程吗?如果是这样,您是否建议在 ASP.NET 中对我的 CPU 密集型操作使用 await async?
  • Task.Run 只是 Factory.StartNew 的简化版本,因此它仍然会使用来自同一个线程池的同一个线程。 Async-await 是在 ASP.NET(以及其他任何地方)中使用 I/O 的方式。计算绑定代码仍应在某个线程上执行,因此您是否会增加用户等待时间(使用单线程)或减少可伸缩性(使用更多 Task 线程)是您自己的选择。
  • @AshishYengkhom Both StartNewRun 如果通过默认任务调度程序,将在线程池线程中运行委托。当然,两者都可以通过任务调度程序来运行代码,而无需使用线程池线程。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-04
  • 1970-01-01
  • 2010-10-11
  • 1970-01-01
  • 2021-01-06
相关资源
最近更新 更多