Parallel.ForEach和DbContext

时间:2014-04-21 11:20:42

标签: c# multithreading entity-framework parallel-processing parallel.foreach

我正在使用Parallel.ForEach并且它极大地提高了我的代码的性能,但我对使用多个线程的DbContext感到好奇。我知道它不是线程安全的,所以我在需要的地方使用锁。

循环遍历字典并计算统计数据:

Dictionary<string, List<decimal>> decimalStats = new Dictionary<string, List<decimal>>(); // this gets populated in another irrelevant loop

List<ComparativeStatistic> comparativeStats = db.ComparativeStatistics.ToList();
var statLock = new object();

Parallel.ForEach(decimalStats, entry =>
{
    List<decimal> vals = ((List<decimal>)entry.Value).ToList();

    if (vals.Count > 0)
    {
        string[] ids = entry.Key.Split('#');
        int questionId = int.Parse(ids[0]);
        int yearId = int.Parse(ids[1]);
        int adjacentYearId = int.Parse(ids[2]);

        var stat = comparativeStats.Where(l => l.QuestionID == questionId && l.YearID == yearId && l.AdjacentYearID == adjacentYearId).FirstOrDefault();

        if (stat == null)
        {
            stat = new ComparativeStatistic();
            stat.QuestionnaireQuestionID = questionId;
            stat.FinancialYearID = yearId;
            stat.AdjacentFinancialYearID = adjacentYearId;
            stat.CurrencyID = currencyId;
            stat.IndustryID = industryId;

            lock (statLock) { db.ComparativeStatistics.Add(stat); }
        }

        stat.TimeStamp = DateTime.Now;

        decimal total = 0M;
        decimal? mean = null;

        foreach (var val in vals)
        {
            total += val;
        }

        mean = Decimal.Round((total / vals.Count), 2, MidpointRounding.AwayFromZero);

        stat.Mean = mean;
    }
});

db.SaveChanges();

我的问题:当我向数据库添加内容时,为什么我只需要锁定?如果stat永远不为null - 如果它总是已经是数据库条目 - 我可以在没有锁定的情况下运行此循环而没有问题,并且数据库按预期更新。如果某个特定循环的stat为空且我没有锁定,则会抛出System.AggregateException

edit1:我每次尝试打开与数据库的新连接,而不是使用lock,这在添加到数据库时也有效(与上面的循环相同) ,我添加了不同的评论):

Parallel.ForEach(decimalStats, entry =>
{
    List<decimal> vals = ((List<decimal>)entry.Value).ToList();

    if (vals.Count > 0)
    {
        using (var dbThread = new PDBContext()) // new db connection
        {
            string[] ids = entry.Key.Split('#');
            int questionId = int.Parse(ids[0]);
            int yearId = int.Parse(ids[1]);
            int adjacentYearId = int.Parse(ids[2]);

            var stat = comparativeStats.Where(l => l.QuestionID == questionId && l.YearID == yearId && l.AdjacentYearID == adjacentYearId).FirstOrDefault();

            if (stat == null)
            {
                stat = new ComparativeStatistic();
                stat.QuestionnaireQuestionID = questionId;
                stat.FinancialYearID = yearId;
                stat.AdjacentFinancialYearID = adjacentYearId;
                stat.CurrencyID = currencyId;
                stat.IndustryID = industryId;

                dbThread.ComparativeStatistics.Add(stat); // no need for a lock
            }

            stat.TimeStamp = DateTime.Now;

            decimal total = 0M;
            decimal? mean = null;

            foreach (var val in vals)
            {
                total += val;
            }

            mean = Decimal.Round((total / vals.Count), 2, MidpointRounding.AwayFromZero);

            stat.Mean = mean;

            dbThread.SaveChanges(); // save
        }
    }
});

这样做安全吗?我确定实体框架的连接池足够智能,但我想知道是否应该添加任何参数来限制线程/连接的数量。

0 个答案:

没有答案