use*_*119 7 c# parallel-processing multithreading entity-framework 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();
Run Code Online (Sandbox Code Playgroud)
我的问题:为什么我只在向数据库添加内容时才需要锁?如果stat从不为空——如果它总是有一个数据库条目——我可以在没有锁的情况下运行这个循环,没有问题,并且数据库会按预期更新。如果stat对于特定循环为 null 并且我在那里没有锁,则会System.AggregateException抛出 a 。
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
}
}
});
Run Code Online (Sandbox Code Playgroud)
这样做安全吗?我确信 Entity Framework 的连接池足够智能,但我想知道是否应该添加任何参数来限制线程/连接的数量。
几年后。你是对的。DbContext正确的解决方案是在循环内部创建一个新实例Parallel.ForEach。创建DbContext是轻量级的,但上下文不是线程安全的。话虽如此,将上下文的生命周期限制为必要的最短持续时间确实很重要。所以请不要让它在请求后长时间打开(ASP.NET)。处理问题时最好的模式DbContexts就是using声明。
有几个可用的文档:
https://learn.microsoft.com/en-us/ef/core/dbcontext-configuration/
https://learn.microsoft.com/en-us/ef/ef6/fundamentals/working-with-dbcontext
| 归档时间: |
|
| 查看次数: |
6642 次 |
| 最近记录: |