Использование PLINQ для вычисления и обновления значений в корпусе не работает - PullRequest
2 голосов
/ 07 мая 2010

Мне недавно нужно было сделать промежуточную сумму в отчете. Где для каждой группы я упорядочиваю строки, а затем вычисляю промежуточный итог на основе предыдущих строк в группе. Ага! Я думал, идеальный вариант использования для PLINQ!

Однако, когда я написал код, у меня появилось странное поведение. Значения, которые я изменял, показывались как измененные при переходе через отладчик, но когда к ним обращались, они всегда были нулевыми.

Пример кода:

class Item
{
 public int PortfolioID;
 public int TAAccountID;
 public DateTime TradeDate;
 public decimal Shares;
 public decimal RunningTotal;
}

List<Item> itemList = new List<Item>
{
 new Item
 {
  PortfolioID = 1,
  TAAccountID = 1,
  TradeDate = new DateTime(2010, 5, 1),
  Shares = 5.335m,
 },
 new Item
 {
  PortfolioID = 1,
  TAAccountID = 1,
  TradeDate = new DateTime(2010, 5, 2),
  Shares = -2.335m,
 },
 new Item
 {
  PortfolioID = 2,
  TAAccountID = 1,
  TradeDate = new DateTime(2010, 5, 1),
  Shares = 7.335m,
 },
 new Item
 {
  PortfolioID = 2,
  TAAccountID = 1,
  TradeDate = new DateTime(2010, 5, 2),
  Shares = -3.335m,
 },

};

var found = (from i in itemList
   where i.TAAccountID == 1
   select new Item
   {
    TAAccountID = i.TAAccountID,
    PortfolioID = i.PortfolioID,
    Shares = i.Shares,
    TradeDate = i.TradeDate,
    RunningTotal = 0
   });

found.AsParallel().ForAll(x =>
{
 var prevItems =  found.Where(i => i.PortfolioID == x.PortfolioID
  && i.TAAccountID == x.TAAccountID 
  && i.TradeDate <= x.TradeDate);
 x.RunningTotal = prevItems.Sum(s => s.Shares);
});

foreach (Item i in found)
{
 Console.WriteLine("Running total: {0}", i.RunningTotal);
}

Console.ReadLine();

Если я изменю выбор для найденного на .ToArray(), он будет работать нормально, и я получу рассчитанные результаты.

Есть идеи, что я делаю не так?

1 Ответ

4 голосов
/ 07 мая 2010

Когда ваш запрос PLINQ выполняется, "found" IEnumerable<T> не был выполнен полностью. Поскольку LINQ to Objects по умолчанию использует отложенное выполнение, каждый элемент «Found» не будет создан, пока запрос PLINQ не достигнет этой позиции.

Так как метод ForAll выполняется с использованием внутреннего поиска, он получает невыполненную или только частично перечисляемую последовательность. Добавляя .ToArray () (или ToList - по сути, все, что заставляет выполнять запрос LINQ to Objects) перед вызовом .ForAll, вы заставляете запрос LINQ to Objects выполняться, что позволяет правильно выполнять запрос PLINQ .

...