【发布时间】:2022-02-01 02:21:04
【问题描述】:
在 C# 中,我使用Task Parallel Library (TPL) 来下载图像、处理图像并保存分析结果。简化代码如下。
var getImage = new TransformBlock<int, Image>(GetImage);
var proImage = new TransformBlock<Image, double>(ProcessImage);
var saveRes = new ActionBlock<double>(SaveResult);
var linkOptions = new DataflowLinkOptions() { PropagateCompletion = true };
getImage.LinkTo(proImage, linkOptions);
proImage.LinkTo(SaveRes, linkOptions);
for (int x = 0; x < 1000000; x++)
getImage.Post(x);
getImage.Complete();
SaveRes.Completion.Wait();
这按预期工作,但内存使用情况除外。我期待int_x、image_x 和double_x 在管道处理完该迭代后被释放。换句话说,我希望在执行getImage、proImage 和saveRes 期间创建的每个资源用于迭代x 在最后一个块完成执行时被释放。但是,此实现将所有对象保留在内存中,直到我退出 TPL 范围。
我错过了什么吗?这是 TPL 的预期行为吗?是否有任何选项可以设置,以便在每次迭代结束时释放资源?
更新
按照 cmets 中的建议,我使用 BufferBlock 和 SendAsync 重写了代码,如下所示。但是,我认为这不会导致要求每个任务消耗的资源。设置BoundedCapacity 只会导致我的程序在我认为它已达到BoundedCapacity 设置的限制时停止。
var blockOpts = new DataflowBlockOptions()
{ BoundedCapacity = 100 };
var imgBuffer = new BufferBlock<int>(blockOpts);
var getImage = new TransformBlock<int, Image>(GetImage, blockOpts);
var proImage = new TransformBlock<Image, double>(ProcessImage, blockOpts);
var SaveRes = new ActionBlock<double>(SaveResult, blockOpts);
var linkOptions = new DataflowLinkOptions() { PropagateCompletion = true };
imgBuffer.LinkTo(getImage, linkOptions);
getImage.LinkTo(proImage, linkOptions);
proImage.LinkTo(SaveRes, linkOptions);
for (int x = 0; x < 1000000; x++)
await imgBuffer.SendAsync(x);
getImage.Complete();
SaveRes.Completion.Wait();
【问题讨论】:
-
您是否尝试过使用合理的
BoundedCapacity配置数据流块?这是一个相关的问题:TPL Dataflow - very fast producer, not so fast consumers OutOfMemory exception. -
如果你使用
BoundedCapacity配置,而ProcessImage/SaveResult方法会抛出异常,建议你看看这个问题:TPL Dataflow exception in transform block with bounded capacity。 -
Hamed 是的,如果您省略处置
IDisposable对象,则不会处置该对象。它持有的资源直到被垃圾回收器最终确定后才会被释放(假设所有对该对象的引用都已被丢弃,以便该对象可以被回收)。 -
老实说,我不喜欢在数据流管道中将
IDisposable对象作为消息传递。通过使用using语句或try/finally块,您可以轻松地保证一次性对象被放置在创建它的方法中。但是如果让它逃到外面,处理它的责任就不再是局部的,确保它最终被处理就变得棘手了。我没有简单的解决方案来解决这个问题。 -
这是个好建议,谢谢!
标签: c# memory-management task-parallel-library tpl-dataflow