1

在我没有使用的测试中Parallel.ForEach(),处理了 3500 条记录。

但是使用 Parellel,每次尝试都会处理随机数量较少的记录。(3402、3375、3471 等)。我发现 DBSet 不支持并行编程,所以我将我的项目移动到一个列表中,然后再处理该列表。(不理想)一些丢失的记录作为空对象添加到列表中。

我错过了什么?

       public int Execute(CalculateAutoClassRatesCommandRequest request)
        {
            int recordCount = 0;
            var autoClassCalculations = new List<AutoClassCalculation>();
            var options = new ParallelOptions { MaxDegreeOfParallelism = 2 };
            Parallel.ForEach(request.BaseCalculations, options, baseCalculation =>
                {
                    foreach (var autoClass in request.AutoClasses
                        .Where(autoClass => baseCalculation.BaseClassId == autoClass.BaseClassId)
                        .Where(autoClass => autoClass.AutoClassForms != null))
                    {
                        foreach (var form in autoClass.AutoClassForms)
                        {
                            recordCount++;
                            var calcRate = CreateAutoClassCalculation(autoClass.AutoClassRoundingRules, form, baseCalculation); 
autoClassCalculations.Add(calcRate);
                        }
                    }
                });

            foreach (var autoClassCalculation in autoClassCalculations)
            {
                request.AutoClassCalculationsDbSet.Add(autoClassCalculation);
            }
            request.ratingContext.SaveChangesWithChangeTracking(request.Identity);

            return recordCount;
        }

private static AutoClassCalculation CreateAutoClassCalculation(
    IEnumerable<AutoClassRoundingRule> autoClassRoundingRules, 
    AutoClassForm autoClassForm,
    BaseCalculation baseCalculation)
{
    var autoClassCalculation = new AutoClassCalculation()
    {   
        BaseCalculation = baseCalculation, 
        AutoClassForm = autoClassForm, 
        BaseCalculationId = baseCalculation.BaseCalculationId, 
        AutoClassFormId = autoClassForm.AutoClassFormId
    };

    var roundingRule = autoClassRoundingRules != null ? autoClassRoundingRules.FindMatchingAutoClassRoundingRule(autoClassForm.AutoClassId, autoClassCalculation.CalcRate) : new AutoClassRoundingRule();
    autoClassCalculation.CalculateRate(roundingRule);

    return autoClassCalculation;
}
4

1 回答 1

3

我在您的代码中看到了一个问题和一个潜在问题:


所有线程都recordCount直接访问变量。虽然num++可能看起来像原子操作,但它不是。它本质上等同于num = num + 1。由于这是在多个 CPU 指令中执行的,因此线程可能会获取一个值,并在更新该值之前被挂起。同时,其他线程增加该值,一旦初始线程恢复,它只是放置它根据旧的起始值计算的值。您应该使用Interlocked.Increment()函数,而不是使用递增运算符。

Interlocked.Increment(ref recordCount);

您的集合的枚举器可能不是线程安全的。如果您使用自定义集合类型,对元素的并行访问可能会比您预期的更快地迭代它们。不过,这不应该引起您描述的问题。明智的做法是使用BlockingCollection<T>.

于 2013-09-09T14:05:54.333 回答