所以,我在这里有一个非常奇怪的情况,似乎forAll()plinq-query删除了我的一些自定义对象,说实话,我不知道为什么。
var myArticles = data.FilterCustomerArticles([]params]).ToList(); //always returns 201 articles
result.Articles = new List<ArticleMinimal>();
try
{
myArticles.AsParallel().ForAll(article =>
{
result.Articles.Add(new ArticleMinimal()
{
ArticleNumber = article.ArticleNumber,
Description = article.Description,
IsMaterial = false,
Price = article.PortionPrice.HasValue ? article.PortionPrice.Value : decimal.Zero,
Quantity = 1,
ValidFrom = new DateTime(1900, 1, 1),
ValidTo = new DateTime(2222, 1, 1)
});
});
}
catch (Exception ex)
{
...
}
以上代码几乎每次调用时都会返回不同的结果计数。
应该返回201 ArticleMinimal
- 对象。相反,它返回200,189,19x ......但不时地返回201。没有异常,没有。它只返回较少的对象。
将代码更改为&#34; good ol&#39;&#34;优雅的foreach循环,我总是得到预期的201个对象。
工作代码:
var myArticles = data.FilterCustomerArticles([]params]).ToList(); //always returns 201 articles
result.Articles = new List<ArticleMinimal>();
try
{
foreach (var article in myArticles) {
result.Articles.Add(new ArticleMinimal()
{
ArticleNumber = article.ArticleNumber,
Description = article.Description,
IsMaterial = false,
Price = article.PortionPrice.HasValue ? article.PortionPrice.Value : decimal.Zero,
Quantity = 1,
ValidFrom = new DateTime(1900, 1, 1),
ValidTo = new DateTime(2222, 1, 1)
});
}
}
catch (Exception ex)
{
...
}
此外,在更多代码行之后,我还有另一个forAll
,如下所示:
try
{
result.Articles.AsParallel().ForAll(article =>
{
if (article.Weight != null){
...
}
});
}
catch (Exception)
{
...
}
使用第一个forAll
,这会抛出一个NullReferenceException
- imho,因为它需要201个对象,但有些Listentries为空。
现在我的实际问题是:为什么第一个forAll
返回的对象少于它应该的?!我能想到的唯一线索是new ArticleMinimal(){ ...});
的内联声明 - 但即使这是因为它对我来说似乎很奇怪。使用plinq时是不是可以这样做?我只是在这里猜测。
希望你能提供帮助。
祝你好运, DOM
答案 0 :(得分:9)
您无法操纵许多线程中的result.Articles
,因为这可能会破坏内部,正如您所观察到的那样。
而是将并行工作流转换为返回创建的对象的管道:
result.Articles.AddRange(myArticles.AsParallel().Select(article =>
new ArticleMinimal()
{
ArticleNumber = article.ArticleNumber,
Description = article.Description,
IsMaterial = false,
Price = article.PortionPrice.HasValue ? article.PortionPrice.Value : decimal.Zero,
Quantity = 1,
ValidFrom = new DateTime(1900, 1, 1),
ValidTo = new DateTime(2222, 1, 1)
})
);
此处的.Select
,因为ParallelQuery
在.AsParallel()
返回的.AddRange
上执行,会在项目上并行运行。
然而,ParallelQuery.GetEnumerator()
会要求.AddRange()
将收集的项目返回到一个长集合中,为您提供所需内容。
根本区别在于,matching_col
可能不会添加任何内容,直到所有并行任务开始完成,而您的方式,如果添加适当的锁定,将在集合生成时添加项目。但是,除非您想要观察在生产过程中流入集合的物品,否则这不一定意味着您的情况。
答案 1 :(得分:5)
List.Add
不是线程安全的。请参阅https://stackoverflow.com/a/8796528/98491
使用任一锁
lock (result.Articles)
{
result.Articles.Add(...);
}
或线程安全集合。我会使用临时集合,最后使用result.Articles.AddRange(...)