Улучшение производительности благодаря многопоточности - PullRequest
2 голосов
/ 01 декабря 2010

Я сейчас пытаюсь улучшить производительность приложения 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
   }
}

Я хочу использовать ThreadPool для каждого элемента данных.

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

Но так много не поточнобезопасных переменных! Я не могу объявить их в каком-либо методе, потому что он разделяется между StepN методами И довольно сложно сделать их все потокобезопасными! Я делаю что-то не так? Есть хорошие решения? Спасибо!

Ответы [ 4 ]

3 голосов
/ 01 декабря 2010

Попробуйте использовать ParallelEnumerable.AsParallel .

data.AsParallel.ForEach(DoWork);

Это автоматически создаст потоки в зависимости от количества процессоров / ядер.Единственная проблема, что он включен в Framework 4.0.Больше информации о PLINQ .(И как прокомментировал andras : для framwork 3.5 он доступен как автономный Reactive Extensions (Rx) )

UPD: как сказал 0xA3, рефакторинг коданастоятельно рекомендуется, чтобы у каждого элемента были свои переменные calc.Я предлагаю вам извлечь логику вычислений в DataItem

Или создать специальный класс, такой как «Калькулятор», который будет выполнять всю работу, чтобы DataItem сохранял только данные, а логика вычислений содержалась бы в классе Calculator.

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

где класс калькулятора примерно такой

class Calculator
{
   // variables here

  void DoWork(DataItem item)
  {
     Step1(item);
     Step2(item);
     // ...
     // StepN(item);
  }
}
0 голосов
/ 01 декабря 2010

Есть ли MainClass в вашей теме GUI? Вы не должны делать какую-либо обработку данных в вашем потоке GUI; запустить MainClass в отдельном потоке.

Как это сделать? Это полностью зависит от того, что blabla вы не показали нам. MainClass нужно вернуть результат? Используйте BeginInvoke / EndInvoke. Вам нужно обновить графический интерфейс? Используйте BackgroundWorker. Если вы хотите получить лучший ответ, вам придется дать нам больше информации.

0 голосов
/ 01 декабря 2010

Поскольку каждый DataItem независим, переместите работу в новый рабочий метод DataItem и позвольте каждому экземпляру иметь дело с самим собой:

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
    }
}
0 голосов
/ 01 декабря 2010

Вероятно, наилучшим способом было бы провести рефакторинг вашего кода, чтобы вы избавились от всех этих полей, общих для разных элементов данных.соответствующие данные и методы для манипулирования 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)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...