【问题标题】:Improve the performance by multi-threading通过多线程提高性能
【发布时间】:2011-05-18 10:45:33
【问题描述】:

我现在正试图通过使其成为多线程来提高 winform 应用程序的性能。目前该类看起来像:

public class MainClass
{
   List<DataItem> data; //thousands of DataItem, but each is independent

   //and a lot of non-thread-safe variables here,variable1 variable2 ...

   public void Go()
   {
      data.ForEach(item => DealWithDataItem(item));
   }

   public void DealWithDataItem(DataItem item)
   {
      //costs really long time here
      Step1(item);
      Step2(item); //and a lot of StepN(item)
   }

   public void StepN(DataItem item)
   {
      //variable1 = blabla
      //variable2 = blabla ..etc
   }
}

我想为每个 DataItem 使用ThreadPool。

data.ForEach(item => ThreadPool.QueueUserWorkItem( s => DealWithDataItem(item) ));

但是有这么多非线程安全的变量!我不能在某些方法中声明它们,因为它在 StepN 方法之间共享。而且很难让它们都成为线程安全的!我做错什么了吗?有什么好的解决办法吗?谢谢!

【问题讨论】:

  • backgroundworker 不够用吗?
  • 这真的取决于你对你的变量做了什么1..n他们对值求和吗?你读书吗?写?您需要更具体地了解这些变量的使用。不同的使用模式有不同的解决方案。
  • 不太明白。是MainClass 某种监视器,用于处理所有DataItem 进度的整体进度(因此您需要它们作为MainClass 的状态)。如果没有,请考虑为您的每项工作创建单独的对象。
  • 在您进一步了解这一点之前,了解这一点很重要:是什么让 Step1 和 Step2 真的很慢?他们在做什么工作?很多运行缓慢的东西都会这样做,因为它们依赖于磁盘或网络等缓慢的东西。在这些情况下,跨多个线程拆分工作通常无济于事。一般来说,在确定如何并行化工作时,工作的性质很重要——你用“blabla ..etc”省略的部分实际上是最重要的部分——你隐藏了人们需要看到才能回答的部分你的问题。
  • 你说变量在StepN方法之间是共享的,但是它们是在不同的线程之间共享的吗?如果不是,那么您收到的答案会有所帮助,但如果它们是,那么您有一个更大的问题......

标签: c# .net multithreading thread-safety threadpool


【解决方案1】:

尝试使用ParallelEnumerable.AsParallel。

data.AsParallel.ForEach(DoWork);

它将根据处理器/内核的数量自动创建线程。 唯一的问题是它包含在 Framework 4.0 中。 有关PLINQ 的更多信息。 (正如 andras 评论的那样:对于框架 3.5,它可以作为独立的 Reactive Extensions (Rx) 使用)

UPD:正如 0xA3 所说,强烈建议重构代码,使每个项目都有自己的计算变量。 我建议你将计算逻辑提取到DataItem

或者创建一个像“计算器”这样的特殊类,它会做所有的工作,所以DataItem只存储数据,计算的逻辑将包含在Calculator类中。

data.AsParallel.ForEach(x=> new Calculator().DoWork(x));

Calculator 类是这样的

class Calculator
{
   // variables here

  void DoWork(DataItem item)
  {
     Step1(item);
     Step2(item);
     // ...
     // StepN(item);
  }
}

【讨论】:

【解决方案2】:

也许最好的方法是重构您的代码,这样您就可以摆脱在不同数据项之间共享的所有字段。

更改(或子类化)DataItem 类以包含操作dataItem 的所有相关数据和方法,以便您的代码更改为如下内容:

public void DealWithDataItem(DataItem item)
{
    item.Step1(); // does not change the state of `this` 
                  // and only changes variables that are private to `item`
    item.Step2(); // and a lot of StepN(item)
}

【讨论】:

    【解决方案3】:

    由于每个DataItem都是独立的,所以将工作移到一个新的DataItem worker方法中,让每个实例自己处理:

    public class MainClass
    {
        List<DataItem> data; //thousands of DataItem, but each is independent
    
        public void Go()
        {
            data.ForEach(item => ThreadPool.QueueUserWorkItem(s => s.DealWithSelf()));
        }
    }
    
    public class DataItem
    {
        //and a lot of non-thread-safe variables here,variable1 variable2 ...
    
        void DealWithSelf()
        {
            //costs really long time here
            Step1(item);
            Step2(item); //and a lot of StepN(item)
        }
    
        public void StepN(DataItem item)
        {
            //variable1 = blabla
            //variable2 = blabla ..etc
        }
    }
    

    【讨论】:

      【解决方案4】:

      MainClass 在您的 GUI 线程中吗?您不应该在您的 GUI 线程中进行任何数据处理;在单独的线程中运行 MainClass。

      如何做到这一点?这完全取决于您未向我们展示的blabla 内容。 MainClass 需要返回结果吗?使用BeginInvoke/EndInvoke。是否需要更新 GUI?使用BackgroundWorker。如果您想要更好的答案,您必须向我们提供更多信息。

      【讨论】:

        猜你喜欢
        • 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
        相关资源
        最近更新 更多