我想使用Parallel.Foreach机制来确保CPU充分利用CPU密集型任务。我一次从数据库中查询大量对象(每次迭代中只有一个对象,每个对象相当小),然后对该对象执行大量基于CPU的操作,之后我将其保存回来到数据库。
我在数据模型方面使用实体框架,并且考虑到我查询的对象数量,我为每次迭代创建一个新的Context(这是为了限制内存消耗):
foreach (var id in idlist)
{
using (var ctx = new Context())
{
var model = ctx.Models.First(x => x.Id == id);
await model.GlobalRefresh(true); //CPU heavy operation.
await model.SaveAsync(); //Additional CPU heavy operation.
ctx.SaveChanges(); //Save the changes
} //Dispose of the model and the context, to limit memory consumption
}
这在同步实现中很有效,因为在每次迭代之后,从数据库查询的模型和实体框架上下文都被处理掉。因此,在此过程中我的内存消耗几乎是恒定的,这很好。如果我不以这种方式创建上下文,我会快速耗尽内存(500多个对象)。
当我按如下方式并行设置上述内容时,我的内存消耗量一直很高,因为似乎每次迭代的上下文都没有在下一次迭代继续之前处理(我确实看到了预期的更好的CPU利用率) :
Parallel.ForEach(idlist, async (id) =>
{
using (var ctx = new Context())
{
var model = ctx.Models.First(x => x.Id == id);
await model.GlobalRefresh(true);
await model.SaveAsync();
ctx.SaveChanges();
}
});
从内存的角度来看,这不一定是个问题,只要所有模型对象不会立即加载到内存中(这实际上也是并行循环的整个点,在一个加载时不止一个时间)。但是,有什么方法可以更好地管理这个过程,例如当内存消耗达到例如75%,以避免Out of Memory异常?
答案 0 :(得分:0)
TL; DR::可以在下次操作之前使用MemoryFailPoint检查是否有足够的内存。 Microsoft Docs有一个很好的示例here。
我最近有一个类似的问题。在检查较旧应用程序的日志时,我注意到一些“内存不足异常”。事实证明,开发人员引入了一些并行编程,以使应用程序更快。因此,也许该应用程序运行速度更快,但是它现在消耗的内存速度更快,并且达到了memory limit for 32-bit app(大约2GB),然后出现了内存不足的异常情况。