如何监控 TPL 数据流网格中的进度?
How do I monitor progress in a TPL Dataflow mesh?
我正在开发一个 C# 应用程序,它具有必须异步执行的耗时顺序工作流。它在用户按下按钮时启动,应用程序会在几毫秒内接收到从相机捕获的一些图像。然后是工作流程。
- 将图像保存到磁盘
- 对齐它们。
- 从中生成 3d 数据。
- 将它们组合成一个更大的集体对象(称为 "Scan")。
- 向此扫描添加可选的分析数据并执行它。
- 最后保存扫描本身,与图像一起保存到 xml 文件中。
其中一些步骤是可选的和可配置的。
由于处理可能需要很长时间,所以通常会有 "scans" 队列等待处理所以我需要向用户呈现捕获扫描队列的可视化表示,它们当前的处理状态(例如 "Saving"、"Analyzing"、"Finished" 等)
我研究过为此使用 TPL DataFlow。但是,虽然网格创建起来很简单,但我并不知道如何监控正在发生的事情的状态,以便我可以更新用户界面。我是否尝试 link 自定义操作块 post 将消息返回给 UI?还有别的吗?
TPL Dataflow 是否适合这项工作?
报告整体进度
当您考虑到 TPL 数据流图有开始和结束块并且您知道有多少项目发布到图中时,您需要做的就是跟踪有多少消息到达了最后一个块并将其与发布到头部的消息的来源计数。这将允许您报告进度。
现在,如果块是 1:1,这就很简单了——也就是说,对于任何消息,都有一条消息输出。如果存在 one:many 障碍,您将需要相应地更改您的进度报告。
报告作业阶段进度
如果您希望在整个图表中显示作业的进度,您需要将作业详细信息传递到每个块,而不仅仅是实际块所需的数据。作业是一项必须跨越问题中列出的所有步骤 1-6 的单一任务。
因此,例如 步骤 2 可能需要 图像数据 才能执行对齐,但它不关心 文件名;工作中有多少步骤或与工作相关的任何其他内容。没有足够的细节来了解当前作业的状态,或者难以仅根据块输入查找原始作业。您可以参考一些外部词典,但最好在图形被隔离并仅处理传递到每个块的数据时设计它们。
所以一个简单的例子就是改变这个最小的代码:
var alignmentBlock = new TransformBlock<Image, Image>(n => { ... });
...至:
var alignmentBlock = new TransformBlock<Job, Job>(x =>
{
job.Stage = Stages.Aligning;
// perform alignment here
job.Aligned = ImageAligner.Align (x.Image, ...);
// report progress
job.Stage = Stages.AlignmentComplete;
});
...并对其他块重复该过程。
阶段 属性 可以触发 PropertyChanged
通知或使用适合您的 UI.
的任何其他形式的通知模式
备注
现在您会注意到我引入了一个 Job
class 作为唯一参数传递给每个块。 Job
包含块的输入数据以及作为块输出的容器。
现在这行得通了,但我的纯粹主义者觉得最好将作业元数据与 TPL 块输入和输出分开,否则多线程可能会损坏状态。
要解决这个问题,您可能需要考虑使用 Tuple<>
并将其传递到块中。
例如
var alignmentBlock = new TransformBlock<Tuple<Job, UnalignedImages>,
Tuple<Job, AlignedImages>>(n => { ... });
我正在开发一个 C# 应用程序,它具有必须异步执行的耗时顺序工作流。它在用户按下按钮时启动,应用程序会在几毫秒内接收到从相机捕获的一些图像。然后是工作流程。
- 将图像保存到磁盘
- 对齐它们。
- 从中生成 3d 数据。
- 将它们组合成一个更大的集体对象(称为 "Scan")。
- 向此扫描添加可选的分析数据并执行它。
- 最后保存扫描本身,与图像一起保存到 xml 文件中。
其中一些步骤是可选的和可配置的。
由于处理可能需要很长时间,所以通常会有 "scans" 队列等待处理所以我需要向用户呈现捕获扫描队列的可视化表示,它们当前的处理状态(例如 "Saving"、"Analyzing"、"Finished" 等)
我研究过为此使用 TPL DataFlow。但是,虽然网格创建起来很简单,但我并不知道如何监控正在发生的事情的状态,以便我可以更新用户界面。我是否尝试 link 自定义操作块 post 将消息返回给 UI?还有别的吗?
TPL Dataflow 是否适合这项工作?
报告整体进度
当您考虑到 TPL 数据流图有开始和结束块并且您知道有多少项目发布到图中时,您需要做的就是跟踪有多少消息到达了最后一个块并将其与发布到头部的消息的来源计数。这将允许您报告进度。
现在,如果块是 1:1,这就很简单了——也就是说,对于任何消息,都有一条消息输出。如果存在 one:many 障碍,您将需要相应地更改您的进度报告。
报告作业阶段进度
如果您希望在整个图表中显示作业的进度,您需要将作业详细信息传递到每个块,而不仅仅是实际块所需的数据。作业是一项必须跨越问题中列出的所有步骤 1-6 的单一任务。
因此,例如 步骤 2 可能需要 图像数据 才能执行对齐,但它不关心 文件名;工作中有多少步骤或与工作相关的任何其他内容。没有足够的细节来了解当前作业的状态,或者难以仅根据块输入查找原始作业。您可以参考一些外部词典,但最好在图形被隔离并仅处理传递到每个块的数据时设计它们。
所以一个简单的例子就是改变这个最小的代码:
var alignmentBlock = new TransformBlock<Image, Image>(n => { ... });
...至:
var alignmentBlock = new TransformBlock<Job, Job>(x =>
{
job.Stage = Stages.Aligning;
// perform alignment here
job.Aligned = ImageAligner.Align (x.Image, ...);
// report progress
job.Stage = Stages.AlignmentComplete;
});
...并对其他块重复该过程。
阶段 属性 可以触发 PropertyChanged
通知或使用适合您的 UI.
备注
现在您会注意到我引入了一个 Job
class 作为唯一参数传递给每个块。 Job
包含块的输入数据以及作为块输出的容器。
现在这行得通了,但我的纯粹主义者觉得最好将作业元数据与 TPL 块输入和输出分开,否则多线程可能会损坏状态。
要解决这个问题,您可能需要考虑使用 Tuple<>
并将其传递到块中。
例如
var alignmentBlock = new TransformBlock<Tuple<Job, UnalignedImages>,
Tuple<Job, AlignedImages>>(n => { ... });