Использование 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 выполняться правильно.,