所以,我有一个非常奇怪的情况,似乎forAll()plinq查询删除了我的一些自定义对象,说实话,我不知道为什么.
var myArticles = data.FilterCustomerArticles([]params]).ToList(); //always returns 201 articles result.Articles = new List(); 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
-Objects.相反,它会返回200,189,19x ......但不时会返回201.没有异常,没有.它只返回少于它应该的对象.
代码更改为"好醇'"优雅的foreach循环之后,我总是得到预期的201个对象.
工作守则:
var myArticles = data.FilterCustomerArticles([]params]).ToList(); //always returns 201 articles result.Articles = new List(); 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是null.
现在我的实际问题是:为什么第一个forAll
返回的对象少于应该的数量?!我能想到的唯一线索是内联声明new ArticleMinimal(){ ...});
- 但即使这是因为它对我来说似乎很奇怪.使用plinq时是不是可以这样做?我只是在这里猜测.
希望你能帮忙.
最好的问候,Dom
您无法操纵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()
在所有并行任务开始完成之前,可能不会添加任何内容,而如果添加适当的锁定,则会在生成时将项添加到集合中.但是,除非您想要观察在生产过程中流入集合的物品,否则在您的情况下这不太重要.
List.Add
不是线程安全的.请参阅/sf/ask/17360801/
使用任一锁
lock (result.Articles) { result.Articles.Add(...); }
或线程安全集合.我会使用临时收藏品并在最后使用result.Articles.AddRange(...)