Использование PLINQ для вычисления и обновления значений в корпусе не работает

Недавно мне нужно было подвести итоги по отчету. Где для каждой группы я упорядочиваю строки, а затем вычисляю промежуточную сумму на основе предыдущих строк в группе. Ага! Я подумал, идеальный вариант использования для 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(), тогда он работает нормально, и я получаю рассчитанные reuslts.

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

1 ответ

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

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

Другие вопросы по тегам