【问题标题】:Concurrently running multiple tasks C#同时运行多个任务C#
【发布时间】:2018-05-16 07:48:35
【问题描述】:

我在 IIS 中有 REST Web API 服务,它接受请求对象的集合。用户可以输入超过 100 个请求对象。

我想同时运行这 100 个请求,然后汇总结果并发回。这涉及 I/O 操作(为每个请求调用后端服务)和 CPU 绑定操作(计算少量响应元素)

代码 sn-p -

using System.Threading.Tasks;
....

var taskArray = new Task<FlightInformation>[multiFlightStatusRequest.FlightRequests.Count];

for (int i = 0; i < multiFlightStatusRequest.FlightRequests.Count; i++)
{
    var z = i;
    taskArray[z] = Tasks.Task.Run(() =>
        PerformLogic(multiFlightStatusRequest.FlightRequests[z],lite, fetchRouteByAnyLeg)
        );
}
Task.WaitAll(taskArray);
for (int i = 0; i < taskArray.Length; i++)
{
    flightInformations.Add(taskArray[i].Result);
}

public Object PerformLogic(Request,...)
{
    //multiple IO operations each depends on the outcome of the previous result
    //Computations after getting the result from all I/O operations
}

如果我单独运行 PerformLogic 操作(针对 1 个对象)需要 300 毫秒,现在我的要求是当我在单个请求中针对 100 个对象运行此 PerformLogic() 时,大约需要 2 秒。

PerformLogic() 有以下步骤 - 1. 调用第 3 方 Web 服务以获取一些详细信息 2. 根据详细信息调用另一个第 3 方 Web 服务 3. 从 Web 服务收集结果,应用少量转换

但是使用 Task.run() 大约需要 7 秒,我想知道处理并发的最佳方法并实现所需的 2 秒 NFR。 我可以看到在任何时间点都有 7-8 个线程同时工作

不确定我是否可以生成 100 个线程或任务,我们可能会看到更好的性能。请提出一种有效处理此问题的方法。

【问题讨论】:

  • 向它抛出更多线程并不能解决整个问题;更多线程并不意味着它执行得更快。在最佳情况下,您的 CPU 可以有效地并行运行与内核一样多的线程。如果您的系统有 7 或 8 个内核,则在您停止看到任何性能优势之前,您可以大致预期 7 或 8 个并发线程。尝试查看 PerformLogic 操作中的瓶颈,并尝试提高效率。
  • 您的 I/O 操作听起来很昂贵。您还可以在允许使用的当前网络连接数量中看到操作系统级别的限制。如果它们少于您拥有的内核数量,您还将失去并行运行它们的好处(因为每个任务都必须等待连接可用)
  • 你有共享状态吗?计算本身在哪里?
  • 如果不知道PerformLogic 方法的内容,就很难回答。
  • PerformLogic() 有以下步骤 - 1. 调用第 3 方 web 服务以获取一些详细信息 2. 根据详细信息调用另一个第 3 方 web 服务 3. 从 web 服务收集结果,应用少量转换

标签: c# asp.net multithreading task multitasking


【解决方案1】:

由此判断

public Object PerformLogic(Request,...)
{
  //multiple IO operations each depends on the outcome of the previous result
  //Computations after getting the result from all I/O operations
}

我敢打赌PerformLogic 大部分时间都花在等待 IO 操作上。如果是这样,async 就有希望了。您将不得不重写PerformLogic,甚至可能是IO 操作——async 需要出现在所有级别,从上到下。但是如果你能做到,结果应该会快很多。

除此之外 - 获得更快的硬件。如果 8 个核心需要 7 秒,那么得到 32 个核心。它很昂贵,但仍可能比重写代码便宜。

【讨论】:

  • 实际上也可能是共享状态。尤其是在意想不到的地方隐藏了lock
  • 为 Vilx 的回答提供更多详细信息:PerformLogic 本身应该是真正的异步,使用异步 IO 方法。然后你可以扔掉 Task.Run 并且你的操作将在不受 CPU 约束的情况下并发运行。
  • @ckuri - 是的。但即便如此,涉及多个 CPU 仍可能提供一些小的提升。我不确定如何最好地去做。也许Task.Run() 仍然是正确的方法。
  • 我同意这个答案。原始代码是一个错误的异步代码,它不会比多线程(这对 I/O 不好)更有效。只有当 PerformLogic 方法完全实现异步允许编译器将其拆分为任务位时,您才能利用异步框架。
【解决方案2】:

首先,不要重新发明轮子。 PLINQ 完全能够并行处理,无需手动处理任务或合并结果。

如果您希望在 2 秒内完成 100 个任务,每个任务需要 300 毫秒,您至少需要 15 个并行工作器,忽略并行化本身的成本

var results = multiFlightStatusRequest.FlightRequests
                                      .AsParallel()
                                      .WithDegreeOfParallelism(15) 
                                      .Select(flightRequest => PerformLogic(flightRequest, lite, fetchRouteByAnyLeg)
                                      .ToList();

现在您已经告诉 PLinq 使用 15 个并发工作人员来处理您的任务队列。你确定你的机器能胜任这项任务吗?您可以在其中输入任何您想要的数字,但这并不意味着您的计算机会神奇地获得执行此操作的能力。

另一种选择是查看您的 PerformLogic 方法并对其进行优化。你调用它 100 次,也许它值得优化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-15
    相关资源
    最近更新 更多