在 UI 线程上运行的 TPL 数据流块
Posted
技术标签:
【中文标题】在 UI 线程上运行的 TPL 数据流块【英文标题】:TPL Dataflow Blocks Running On UI Thread 【发布时间】:2014-05-08 13:03:52 【问题描述】:我正在构建一个数据流管道来执行自然流中的各种处理(主要是 I/O,但也有一些 CPU 处理)。流程目前处于这种基本模式:
-
从文件中加载数据
使用转换块解析记录
通过 REST 将对象序列化并上传到服务器
此处理管道可以自动启动,也可以通过 GUI 启动。因为当它从 GUI 启动时,我想向最终用户提供进度消息。如果我在第 1 步和第 2 步之间添加 BufferBlock
并在第 3 步之后添加 ActionBlock
并将它们设置为在与 UI 相同的线程上运行,其他块是否仍会使用自己的线程池在 UI 上运行?
我正在查看这篇 MSDN 文章:http://msdn.microsoft.com/en-us/library/hh228605(v=vs.110).aspx,但关于这种行为并不是很清楚。我可以从可以在 UI 线程上运行的管道中触发一个事件来完成此操作吗?
编辑:管道将从 UI 上的 BackgroundWorker
对象启动,而不是直接从 UI 线程启动。
【问题讨论】:
您是否使用DataflowBlockOptions.TaskScheduler
使其运行UI 线程?一段代码会有所帮助。
@Noseratio 这更像是一个理论问题。我还在计划这个功能。截至目前,它还没有报告进度。使用DataflowBlockOptions.TaskScheduler
是我要告诉那些特定块在 UI 线程上运行的方式。
我会使用Progress<T>
模式,而不是使用 UI 线程 TaskScheduler。
我认为你在这里不需要BackgroundWorker
。只需在 UI 线程上创建 Progress<T>
(它确实捕获 s. 上下文并在内部使用 sc.Post
)。然后在取得进展的任何地方从您的 Dataflow 管道中调用它。
@Noseratio 感谢您的建议。我会调查一下。
【参考方案1】:
多亏了 Noseratio 的建议,我实际上重新设计了它是如何完成的,并且能够让它毫无问题地工作。我删除了 BackgroundWorker
对象,因为它并不是真正需要的。相反,我将整个数据流包装在一个异步方法中,该方法将各种Progress<T>
参数作为进度更新的回调。由于Progress<T>
的Report()
方法在预先存在的块中被调用,因此没有使用额外的TPL 块来发布进度。此外,由于这是一个异步函数,表示数据流的任务不在 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 都会毫无问题地更新。
【讨论】:
以上是关于在 UI 线程上运行的 TPL 数据流块的主要内容,如果未能解决你的问题,请参考以下文章