简体   繁体   English

如何监视TPL Dataflow网格中的进度?

[英]How do I monitor progress in a TPL Dataflow mesh?

I'm working on a C# app with a time-consuming sequential workflow that must be performed asynchronously. 我正在使用C#应用程序,该应用程序具有必须异步执行的耗时的顺序工作流。 It starts when the user presses a button and the app receives a few images captured from a camera within just a few milliseconds. 它在用户按下按钮时启动,并且该应用在短短几毫秒内收到从相机捕获的一些图像。 The work flow then. 然后的工作流程。

  1. Saves the images to disk 将图像保存到磁盘
  2. Aligns them. 对齐它们。
  3. Generates 3d data from them. 从它们生成3d数据。
  4. Groups them into a larger, collective object (called a "Scan"). 将它们分组为更大的集合对象(称为“扫描”)。
  5. Add optional analysis data to this scan and executes it. 将可选的分析数据添加到此扫描并执行它。
  6. Finally saves the scan itself is saved to an xml file alongside the images. 最终保存扫描本身,并与图像一起保存到xml文件中。

Some of these steps are optional and configurable. 其中一些步骤是可选的和可配置的。

Since the processing can take so long, there will often be a queue of "scans" awaiting processing So I need to present to a user a visual representation of the queue of captured scans, their current processing state (eg "Saving", "Analyzing", "Finished" etc.) 由于处理可能需要很长时间,因此通常会有“扫描”队列等待处理。因此,我需要向用户呈现捕获的扫描队列的可视表示形式,包括它们的当前处理状态(例如“保存”,“分析” “,“完成”等)

I've looked into using TPL DataFlow for this. 我已经研究过为此使用TPL DataFlow。 But while the mesh is simple to create, I'm not getting just how I might monitor the status of what is going on so that I can update a user interface. 但是,尽管网格很容易创建,但我并没有获得如何监视正在进行的状态以便更新用户界面的方式。 Do I try to link custom action blocks that post back messages to the UI for that? 我是否尝试链接自定义操作块,以将其回传到UI的消息? Something else? 还有吗

Is TPL Dataflow even the right tool for this job? TPL Dataflow甚至是这项工作的正确工具吗?

Reporting Overall Progress 报告总体进展

When you consider that a TPL DataFlow graph has a beginning and end block and that you know how many items you posted into the graph, all you need do is track how many messages have reached the final block and compare it to the source count of messages that were posted into the head. 当您考虑到TPL DataFlow图具有开始和结束块,并且知道向该图发布了多少个项目时,您要做的就是跟踪到达最后一个块的消息数量并将其与消息的源计数进行比较。被张贴到头部。 This will allow you to report progress. 这将允许您报告进度。

Now this works trivially if the blocks are 1:1 - that is, for any message in there is a single message out. 现在,如果块为1:1,这将变得很简单-也就是说,对于其中的任何消息,只有一条消息输出。 If there is a one:many block, you will need to change your progress reporting accordingly. 如果存在一个“很多”块,则需要相应地更改进度报告。

Reporting Job Stage Progress 报告作业阶段进度

If you wish to present progress of a job as it travels throughout the graph, you will need to pass job details to each block, not just the data needed for the actual block. 如果您希望在整个图形中显示作业的进度,则需要将作业详细信息传递给每个块,而不仅是实际块所需的数据。 A job is a single task that must span all the steps 1-6 listed in your question. 一项工作是一项任务,必须跨越问题中列出的所有步骤1-6。

So for example step 2 may require image data in order to perform alignment but it does not care about filenames ; 因此例如第2步可能需要图像数据才能进行对齐,但是它并不在乎文件名 ; how many steps there are in the job or anything else job related. 作业中有多少个步骤或与作业相关的其他任何步骤。 There is insufficient detail to know state about the current job or makes it difficult to lookup the original job based on the block input alone. 没有足够的细节来了解有关当前作业的状态,或者使仅基于块输入来查找原始作业变得困难。 You could refer to some external dictionary but graphs are best designed when they are isolated and deal only with data passed into each block. 您可以参考一些外部词典,但是当图形被隔离并且仅处理传递到每个块中的数据时,它们是最佳设计。

So a simple example would be to change this minimal code from: 因此,一个简单的示例就是将以下最小代码更改为:

var alignmentBlock = new TransformBlock<Image, Image>(n => { ... });

...to: ...至:

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;
});

...and repeat the process for the other blocks. ...并对其他模块重复该过程。

The stage property could fire a PropertyChanged notification or use any other form of notification pattern suitable for your UI. stage属性可以触发PropertyChanged通知或使用适合您的UI的任何其他形式的通知模式。

Notes 笔记

Now you will notice that I introduce a Job class that is passed as the only argument to each block. 现在您将注意到我引入了Job类,该类作为唯一参数传递给每个块。 Job contains input data for the block as well as being a container for block output. Job包含该块的输入数据以及作为块输出的容器。

Now this will work, but the purist in me feels that it would be better to perhaps keep job metadata separate what is TPL block input and output otherwise there is potential state damage from multiple threads. 现在这将起作用,但是我的纯粹主义者认为,最好将作业元数据分开是什么TPL块输入和输出,否则可能会导致多个线程损坏状态。

To get around this you may want to consider using Tuple<> and passing that into the block. 为了解决这个问题,您可能需要考虑使用Tuple<>并将其传递到块中。

eg 例如

var alignmentBlock = new TransformBlock<Tuple<Job, UnalignedImages>, 
                                        Tuple<Job, AlignedImages>>(n => { ... });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM