我有这个代码用于向数据库添加大约1500条记录:
async void button7_Click(object sender, EventArgs e)
{
var task = await Task.Run(() =>
{
Random rnd = new Random();
for (int i = 0; i <= 1500; i++)
{
db.Tbls.Add(new Tbl()
{
Name = "User" + i + 1,
Num = rnd.Next(10, i + 10) / 10
});
progress.Report(i * 100 / 1500);
}
db.SaveChanges();
return db.Tbls.Count();
});
}
但是完成这个过程需要大约4秒钟,但因为我使用了async/await
它并没有冻结用户界面。现在我的问题是:如何改进此代码以更快地运行。我如何在这里使用并行编程?你能告诉我一个如何使用TPL的例子吗?
修改:
我按照建议使用并行循环和AddRange
,但它没有效果。在所有建议的方式中,我的过程仍然需要大约4秒钟。如果有人能帮助我解决这个问题,我真的很感激。
答案 0 :(得分:3)
并行处理在这里不会有很大的帮助,因为你的循环本身就没有时间。所有消耗的时间都分为两个方面:
当您向Entity Framework上下文添加新项目(并且您在此处使用EF)时,它会对上下文中的许多项目执行某些操作(而不仅仅是您添加的项目)并且它会变得越来越慢您添加的项目越多。
Perfoming 1500数据库插入也需要一些时间。
这里最简单的方法是减少上面列表中第1点所花费的时间。你可以这样做:
async void button7_Click(object sender, EventArgs e)
{
var task = await Task.Run(() =>
{
Random rnd = new Random();
var tbls = new List<Tbl>();
for (int i = 0; i <= 1500; i++)
{
tbls.Add(new Tbl()
{
Name = "User" + i + 1,
Num = rnd.Next(10, i + 10) / 10
});
progress.Report(i * 100 / 1500);
}
db.Tbls.AddRange(tbls);
db.SaveChanges();
return db.Tbls.Count();
});
}
使用AddRange
并首先收集简单List
中的所有项目,您将大大减少代码消耗的时间,至少低于1秒。
更新。如果你想使用并行循环,即使这不会有帮助,你也可以这样做:
int seed = Environment.TickCount;
var random = new ThreadLocal<Random>(() => new Random(Interlocked.Increment(ref seed)));
var tbls = new ConcurrentBag<Tbl>();
Parallel.For(0, 1500, (i) => {
tbls.Add(new Tbl()
{
Name = "User" + i + 1,
Num = random.Value.Next(10, i + 10) / 10
});
});
db.Tbls.AddRange(tbls);
注意事项:
Random
不是线程安全的,所以我们使用线程局部实例(并行循环内的每个线程一个),每个实例都有不同的种子值。List
不是线程安全的 - 我们使用ConcurrentBag
代替。答案 1 :(得分:3)
如果性能是目标,那么您应该使用EF以外的其他内容,例如dapper(StackOverflow.com使用的相同内容)或原始ADO.Net。除此之外,您可以通过停用change auto detection和validation on save来提高实体框架的效果:
yourDbContext.Configuration.AutoDetectChangesEnabled = false;
yourDbContext.Configuration.ValidateOnSaveEnabled = false;
您应该将上述代码与使用AddRange
的@Evk建议结合起来。
另一种可能有助于加快插入速度的解决方案是使用BulkInsert(图片来自here):