【问题标题】:What's the correct way to run multiple parallel tasks in an asp.net process?在 asp.net 进程中运行多个并行任务的正确方法是什么?
【发布时间】:2015-03-15 06:42:24
【问题描述】:

我想我不明白什么。我曾认为Task.Yield() 强制为一项任务启动一个新线程/上下文,但在re-reading this answer 上,它似乎只是强制方法异步。它仍将处于相同的上下文中。

在 asp.net 进程中,在不导致死锁的情况下并行创建和运行多个任务的正确方法是什么?

也就是说,假设我有以下方法:

async Task createFileFromLongRunningComputation(int input) { 
    //many levels of async code
}

而当某个POST路由命中时,我想同时启动上述方法3次,立即返回,但是当三个都完成后记录。

我想我需要在我的行动中加入这样的事情

public IHttpAction Post() {
   Task.WhenAll(
       createFileFromLongRunningComputation(1),
       createFileFromLongRunningComputation(2),
       createFileFromLongRunningComputation(3)
   ).ContinueWith((Task t) =>
      logger.Log("Computation completed")
   ).ConfigureAwait(false);
   return Ok();

}

createFileFromLongRunningComputation 需要填写什么内容?我原以为Task.Yield 是正确的,但显然不是。

【问题讨论】:

  • 如果您从未真正使用过await Task,那么调用ConfigureAwait 是毫无意义的。
  • 谢谢@Servy 我有点知道,但我没想到

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


【解决方案1】:

l3arnon's answer 是正确的。这个答案更多的是讨论OP提出的方法是否是好的。)

真的,你不需要什么特别的东西。 createFileFromLongRunningComputation 方法不需要任何特殊的东西,只要确保你在其中 awaiting 一些异步方法,ConfigureAwait(false) 应该避免死锁,假设你没有做任何事情与众不同(可能只是文件 I/O,给定方法名称)。

警告

这是有风险的。如果任务需要太长时间才能完成,ASP.net 很可能会在这种情况下从您身下拉开帷幕。

正如其中一位评论者所指出的,有更好的方法来实现这一点。其中之一是HostingEnvironment.QueueBackgroundWorkItem(仅在 .NET 4.5.2 及更高版本中可用)。

如果长时间运行的计算需要很长时间才能完成,那么最好将其完全排除在 ASP.net 之外。在这种情况下,更好的方法是使用某种消息队列,以及在 IIS/ASP.net 之外处理这些消息的服务。

【讨论】:

  • @GeorgeMauer 那为什么标题是“等待多个任务”。另外,这是非常冒险的。如果你想在asp.net中进行后台处理,有更好的方法。
  • @GeorgeMauer 你所需要的都在这里:hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx
  • @GeorgeMauer:那么你真的不应该在 ASP.NET 中这样做。您应该有一个具有独立后端工作进程的持久队列。
  • 他已经有异步任务了。没有理由将它们包装在对Run 的调用中;它只是浪费时间在线程池中调度一个任务来启动一个异步操作。
【解决方案2】:

将并发工作卸载到不同线程的正确方法是按照 rossipedia 的建议使用Task.Run

ASP.Net 中后台处理的最佳解决方案(您的AppDomain 可以与您的所有任务一起自动回收/关闭)在Scott HanselmanStephen Cleary 中博客(例如 HangFire)

但是,您可以使用Task.YieldConfigureAwait(false) 来实现相同的目的。

所有Task.Yield 所做的只是返回一个等待器,以确保该方法的其余部分不会同步进行(通过让IsCompleted 返回falseOnCompleted 立即执行Action 参数)。 ConfigureAwait(false) 忽略 SynchronizationContext 并因此强制方法的其余部分在 ThreadPool 线程上执行。

如果同时使用两者,您可以确保async 方法立即返回一个任务,该任务将在ThreadPool 线程上执行(如Task.Run):

async Task CreateFileFromLongRunningComputation(int input)
{
    await Task.Yield().ConfigureAwait(false);
    // executed on a ThreadPool thread
}

编辑: George Mauer 指出,由于Task.Yield 返回YieldAwaitable,因此您不能使用ConfigureAwait(false),这是Task 类的方法。

您可以通过使用Task.Delay 以非常短的超时来实现类似的效果,因此它不会是同步的,但您不会浪费太多时间:

async Task CreateFileFromLongRunningComputation(int input)
{
    await Task.Delay(1).ConfigureAwait(false);
    // executed on a ThreadPool thread
}

更好的选择是创建一个YieldAwaitable,它像使用ConfigureAwait(false) 一样简单地忽略SynchronizationContext

async Task CreateFileFromLongRunningComputation(int input)
{
    await new NoContextYieldAwaitable();
    // executed on a ThreadPool thread
}

public struct NoContextYieldAwaitable
{
    public NoContextYieldAwaiter GetAwaiter() { return new NoContextYieldAwaiter(); }
    public struct NoContextYieldAwaiter : INotifyCompletion
    {
        public bool IsCompleted { get { return false; } }
        public void OnCompleted(Action continuation)
        {
            var scheduler = TaskScheduler.Current;
            if (scheduler == TaskScheduler.Default)
            {
                ThreadPool.QueueUserWorkItem(RunAction, continuation);
            }
            else
            {
                Task.Factory.StartNew(continuation, CancellationToken.None, TaskCreationOptions.PreferFairness, scheduler);
            }
        }

        public void GetResult() { }
        private static void RunAction(object state) { ((Action)state)(); }
    }
}

这不是建议,而是对您的 Task.Yield 问题的回答。

【讨论】:

  • 这是实际问题的正确答案。我的回答有更多关于 OPs 方法是否好的讨论点。
  • @rossipedia 确实如此。我想重申,这不是真正的代码应该是什么样子。
  • 哎呀,现在得弄清楚我是否可以说服 infra 部署 4.5.2
  • Task.Yield() 不返回任务,因此没有ConfigureAwait 函数。所以不推荐的方法是行不通的。
  • @GeorgeMauer 你是对的,我没想到。我为答案添加了一个非常有趣的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-07
  • 1970-01-01
  • 1970-01-01
  • 2012-06-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多