我正在构建一个数据流管道来执行自然发生的流中的各种处理(主要是I/O,但有一些CPU处理(。 流当前处于以下基本模式:
- 从文件加载数据
- 使用转换块分析记录
- 通过 REST 序列化和上传对象到服务器
此处理管道可以自动启动,也可以通过 GUI 启动。 当它从GUI启动时,我想向最终用户提供进度消息。 如果我在步骤 1 和 2 以及步骤 3 之后的ActionBlock
之间添加一个BufferBlock
,并设置它们在与 UI 相同的线程上运行的选项,其他块是否仍会使用自己的线程池在 UI 上运行?
我正在查看这篇 MSDN 文章:http://msdn.microsoft.com/en-us/library/hh228605(v=vs.110(.aspx但对于这种行为不是很清楚。 是否可以从管道触发也可以在 UI 线程上运行的事件来完成此操作?
编辑:管道将从 UI 上的 BackgroundWorker
对象启动,而不是直接从 UI 线程启动。
多亏了Noseratio的建议,我实际上重新设计了它是如何完成的,并且能够让它毫无问题地工作。 我删除了BackgroundWorker
对象,因为它并不是真正必要的。 相反,我将整个数据流包装在一个异步方法中,该方法将各种Progress<T>
参数作为进度更新的回调。 没有额外的TPL块用于发布进度,因为Progress<T>
的Report()
方法是在预先存在的块中调用的。 此外,由于这是一个异步函数,因此表示数据流的任务不在 UI 线程上运行,而是在线程池线程上运行。 从中得出的主要结论是,Progress<T>
对象的回调是在创建它们的线程上运行的,因为在构造期间它们捕获当前的同步上下文。 以下是解决我的问题的示例:
public static async Task ProcessData(IProgress<int> ReadProg, IProgress<int> UploadProg)
{
var loadBuffer = new BufferBlock<string>();
var parseBlock = new TransformBlock<string, MyObject>(async s =>
{
if(await DoSomething(s))
ReadProg.Report(1);
else
ReadProg.Report(-1);
});
...
//setup of other blocks
//link blocks
//feed data into pipeline by adding data into the head block: loadBuffer
//await ALL continuation tasks of the blocks
}
然后在 UI 中,我创建了Progress<int>
对象并将它们传递给异步ProcessData
方法。 每当在异步处理方法中调用 Process<T>.Report()
方法时,UI 都会更新而不会出现问题。