【问题标题】:Is parallel.foreach is best way to improve speed of executing this code是 parallel.foreach 是提高执行此代码速度的最佳方法吗
【发布时间】:2016-08-19 04:15:23
【问题描述】:

我有代码需要重写以提高执行原始代码的速度:

数据类:

public class Data
{
    public string Id {get;set;}
    ... Other properties 
}

服务:(例如我给你2个以上)

public class SomeService1
{
    public Result Process(Data data)
    {
        //Load data from different services hire 
    }
}

public class SomeService2
{
    public Result Process(Data data)
    {
        //Load data from different services hire 
    }
}

实际方法

public void Calculate (List<Data> datas)
{
    Result result;
    SomeService1 someService1 = new SomeService1();
    SomeService2 someService2 = new SomeService2();
    // In this place list of data have about 2000 elements 
    foreach(var data in datas)
    {
        switch(data.Id)
        {
            case 1:
                result = someService1.Process(data)
                break;
            case 2:
                result = someService2.Process(data)
                break;
            default:
                result = null;
        }
    ProcesAndSaveDataToDatabase(result);
    }
}

Calculate 方法我将 List 作为该列表中每个元素的参数,它从外部服务获取数据(服务由数据类中的 id 确定)。然后它处理这些数据并保存到数据库。对于 2000 个元素,整个操作大约需要 8 分钟。 70% 的时间是从外部服务收集数据。我必须改变那个时间。我只有一个想法可以做到这一点,但老实说,我无法用数据对其进行测试,因为只有数据在生产环境中(并且在生产环境中进行测试是个坏主意)。我有一个想法。如果我朝着正确的方向前进,你能看看它并给我建议吗?

数据类:

public class Data
{
    public string Id {get;set;}
    ... Other properties 
}

服务:(例如我给你2个以上)

public class SomeService1 : IService
{
    public Result Process(Data data)
    {
        //Load data from different services hire 
    }
}

public class SomeService2 : IService
{
    public Result Process(Data data)
    {
        //Load data from different services hire 
    }
}

服务:

public interface IService
{
    Result Process(Data data);
}

实际方法:

Public void Calculate (List<Data> datas)
{
    var split= from data in datas group data by data.Id into newDatas select newDatas
    // Different list split by Id
    Parallel.Foreach(split, new ParallelOptions{MaxDegreeOfParallelism = 4}, datas => 
    {
        Result result;
        IService service = GetService(datas.FirsOfDefault().Id);
        if(service == null) return;
        foreach(var data in datas)
        {
            result = service.Process(data)
            ProcesAndSaveDataToDatabase(result);
        }
    }); 
}

private IService GetService(string id)
{
      IService service = null;
      if(id == null ) return service;
      switch(id)
      {
           case 1:
                service = new SomeService1();
                break;
           case 2: 
                service = new SomeService2();
                break;
      }
      return service;
 }

在这个想法中,我尝试将不同的服务数据拆分到不同的线程。因此,在列表中,我们将有 20 个带有 Id = 1 的项目和 10 个带有 Id = 2 的项目,它应该创建 2 个分离的线程并离散地处理它,这应该可以让我切断执行时间。这是好方法吗?是否还有其他改进此代码的可能性?

谢谢

【问题讨论】:

  • 如果 70% 的时间都花在从服务中提取数据上,那么这就是您需要优化的地方。您对服务有任何控制权吗?
  • 不。有外部服务,我只有服务参考
  • 所以现在串行提取数据需要大约 5.6 分钟?如果您使该部分并行,它将更快,因为您一次多次访问该服务。此外,在您的本地代码上使用分析器来查看您可以优化什么……但您永远不会比服务可以提供的更快。

标签: c# multithreading optimization


【解决方案1】:

Parallel ForEach 有助于改进 CPU 密集型任务,但您在上面提到您正在并行调用服务,这是 IO 密集型的。每当您进行 IO 绑定工作(例如调用外部服务)时,最好使用 async 和 await 而不是并行 foreach。

Parallel ForEach 将启动多个线程并阻塞这些线程,直到工作完成(所有线程阻塞大约需要 8 分钟)。

Async 和 Await 将在服务调用之间编织工作线程,并有效地使用 IO 完成端口回调到您的应用程序中。这避免了多个线程的阻塞,并允许您更有效地使用计算机资源。

有关如何在此处进行并行异步调用的更多信息:

https://msdn.microsoft.com/en-us/library/mt674880.aspx

【讨论】:

  • 异步的问题是我已经有来自服务的方法(这不是我的),而这个服务只是从 WCF 调用方法。在那种情况下,我必须请求在此服务内进行更改?或者我可以异步调用这个服务?
  • svcutil 可以使用 /async 标志生成异步方法。您不必更改服务实现,它在客户端代理中执行此操作。 Visual Studio 中的“添加服务引用”也有一个用于生成异步方法的复选框。
  • 好的,我使用了异步方法并删除了一些 foreach 并使用 Parallel.Foreach,现在我有 3000 个元素的 2 分钟 30 秒而不是 2000 年的 8 分钟,我相信这是最大值
【解决方案2】:

虽然您将获得在应用程序中使用 Parallelism(Parallel.ForEach) 的好处,但这并不是提高代码执行速度的唯一方法。

此外,由于您在应用程序中使用 LINQ,而且您可能也在广泛使用它,因此您可能希望尽可能使用 PLINQ(Parallel LINQ)。

我还建议您尝试分析您的代码,以确定您的应用程序中的热点瓶颈,这可能会给您更好地了解可以在何处以及如何提高性能。

另外,正如 Patrick 所说,您应该尽可能尝试使用 asyncawait

查看 MSDN 中的这篇文章,它将为您提供更多见解 https://msdn.microsoft.com/en-us/library/ff963552.aspx

【讨论】:

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