【问题标题】:Why does Task.WhenAll occasionally take a long time为什么Task.WhenAll偶尔会花很长时间
【发布时间】:2018-01-24 09:36:03
【问题描述】:

我有一个 ASP.NET 站点,它正在运行适量的请求(大约 500rpm 分散在 3 台服务器上),通常请求大约需要 15 毫秒。但是,我发现经常有一些请求需要更长的时间(1 秒或更多)。我已将延迟缩小到对Task.WhenAll 的调用。这是违规代码的示例:

var taskA = dbA.GetA(id);
var taskB = dbB.GetB(id);
var taskC = dbC.GetC(id);
var taskD = dbD.GetD(id);

await Task.WhenAll(taskA, taskB, taskC, taskD);

每个单独的任务都经过测量,完成时间不到 10 毫秒。我已经将延迟确定为Task.WhenAll 调用,这似乎与任务的安排方式有关。据我所知,TPL任务池的压力并不大,所以我不知道为什么性能如此零星。

【问题讨论】:

  • 我会尝试使用嗅探器,看看互联网上的时间间隔是否与应用程序中的间隔相关。
  • 我已将其归结为对 TPL 的调用。单独的网络请求被测量并且永远不会超过 10 毫秒。正在进行中。
  • @EricAndres 抱歉,这是题外话,但出于我自己的好奇心:您使用什么工具得出这样的结论?
  • 我们正在使用 Stopwatch.StartNew 进行测量,然后将它们发送到 Influx 数据库。
  • 您测量的是Task.WhenAll 还是await Task.WhenAll?因为你会测量非常不同的东西

标签: c# asp.net-mvc task-parallel-library


【解决方案1】:

创建线程需要开销。根据您的操作,您还可以尝试 Parallel.ForEach。

public static void yourMethod(int id){

    var tasks = new List<IMyCustomType> { new dbA.GetA(id), new dbB.GetB(id), new dbC.GetC(id), new dbD.GetD(id)};

    // Your simple stopwatch for timing
    Stopwatch stopWatch = new Stopwatch();
    stopWatch.Start();

    // For each 'tasks' list item, call 'executeTasks' (Max 10 occurrences)
    // - Processing for all tasks will be complete before 
    //   continuing processing on the main thread
    Parallel.ForEach(tasks, new ParallelOptions { MaxDegreeOfParallelism = 10 }, executeTasks);

    stopWatch.Stop();
    Console.WriteLine("Completed execution in:  " + stopWatch.Elapsed.TotalSeconds);
}

private static void executeTasks(string obj)
{
    // Your task's work here. 
}

【讨论】:

  • 是什么让您认为 OP 正在使用多个线程?您所知道的是它们具有异步操作,并且它们正在执行 DB 调用(不需要线程,因此如果 OP 正在使用它们,则没有理由这样做)。 您的 代码正在添加一堆线程工作。不必要的线程工作。该代码也没有接近编译,因为 OP 的方法都返回任务,并且不像您处理它们的方式那样同步。
【解决方案2】:

异步操作涉及上下文切换,这很耗时。不幸的是,并不总是以一种确定的方式。为了加快速度,请尝试在 Task.WhenAll 调用前加上 ConfigureAwait(false),如下所示:

await Task.WhenAll(taskA, taskB, taskC, taskD).ConfigureAwait(false);

这将消除额外的上下文切换,这实际上是服务器端应用程序推荐的方法。

【讨论】:

  • 忘记 ConfigureAwait 不会改变任何关于上下文切换的事实,我们谈论的是整整 的延迟,而不是几微秒
  • 这是我要做的一个实验: 1. 在调用Task.WhenAll 之前,也启动另一个任务(它只会在输出中打印一些内容),然后调用Task.WhenAll。看看新任务是否有相同的延迟或者它会很快再次完成会很有趣,问题确实在 Task.WhenAll 相关
猜你喜欢
  • 2013-03-08
  • 1970-01-01
  • 2010-10-09
  • 2015-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-25
相关资源
最近更新 更多