parallel linq:AsParallel()。forAll()使一些对象为空

时间:2016-01-08 10:09:26

标签: c# .net linq plinq

所以,我在这里有一个非常奇怪的情况,似乎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

2 个答案:

答案 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(...)