【发布时间】:2026-02-23 00:20:06
【问题描述】:
我的目标是加快查询速度,我想利用并行性,假设我在 ids 列表中有 2,000 个项目,我将它们分成 4 个列表,每个列表有 500 个 id,我想打开 4 个踏板每个人都将创建一个数据库调用并统一他们的结果,以实现我使用 Parallel.ForEach,但它并没有提高查询的性能,因为它显然不适合 io 绑定操作:Parallel execution for IO bound operations
if 块中的代码对每个使用并行,而 else 块中的代码在常规 foreach 中执行。
问题是包含这个查询的方法不是异步的(因为它是在一个非常遗留的组件中)并且它不能更改为异步,基本上我想在非异步方法中进行并行 io 绑定计算(通过实体框架)。
实现这一目标的最佳做法是什么?我看到也许我可以为此使用Task.WaitAll(),我不在乎阻塞运行此查询的线程,我更担心从非异步方法调用的Task.WaitAll() 会出现问题
我使用实体框架作为 SQL 数据库上的 ORM,我为每个线程打开一个单独的上下文,因为上下文不是线程安全的。
也许我使用的锁是导致我出现问题的锁,我可以将其更改为ConcurrentDictionary。
下面代码中描述的场景是我需要改进的场景的简化,在我们的实际应用程序中,我确实需要在加载 id 后读取相关实体,并对它们执行复杂的计算。
代码:
//ids.Bucketize(bucketSize: 500) -> split one big list, to few lists each one with 500 ids
IEnumerable<IEnumerable<long>> idsToLoad = ids.Bucketize(bucketSize: 500);
if (ShouldLoadDataInParallel())
{
object parallelismLock = new object();
Parallel.ForEach(idsToLoad,
new ParallelOptions { MaxDegreeOfParallelism = 4 },
(IEnumerable<long> bucket) =>
{
List<long> loadedIds = GetIdsQueryResult(bucket);
lock (parallelismLock)
{
allLoadedIds.AddRange(loadedIds );
}
});
}
else
{
foreach (IEnumerable<long> bucket in idsToLoad)
{
List<long> loadedIds = GetIdsQueryResult(bucket);
allLoadedIds.AddRange(loadedIds);
}
}
【问题讨论】:
-
你没有。您改为修复数据访问代码。使用 EF Core 或任何 ORM 按 ID 执行批量删除毫无意义。本案例不涉及任何对象
-
此外,并行执行批处理操作只会增加阻塞,不会减少阻塞。您仍然使用相同的磁盘、相同的 CPU、相同的事务日志。
-
你需要执行的SQL是
DELETE SomeTable where ID in (...)。 EF Core 无法做到这一点,但 Dapper 可以。您可以在 DbContext 的连接上使用 Dapper 来执行例如Execute("delete theTable where ID in (@ids):, new {ids=someList}")。重复执行以删除批次而不会淹没事务日志 -
the delete was just to simplify the use case,在这种情况下,问你真正的问题并描述真正的问题。细节很重要。唯一可以确定的是“并行化”只会损害性能。通常呈指数增长 -
此外,如果数据来自数据库,您可以修改
SELECT查询以实际删除匹配的行。没有理由将数据拉到客户端只是为了发回 DELETE。
标签: c# entity-framework async-await task-parallel-library parallel.foreach