我目前正在处理一个从大型二进制文件读取的应用程序,该文件包含数千个文件,每个文件正由应用程序中的其他类处理。该类返回一个对象或null。我想在主表单上显示进度,但出于某种原因,我无法理解它。
int TotalFound = 0;
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext;
BufferBlock<file> buffer = new BufferBlock<file>();
DataflowBlockOptions options = new DataflowBlockOptions(){ TaskScheduler = uiScheduler, };
var producer = new ActionBlock<someObject>(largeFile=>{
var file = GetFileFromLargeFile(largeFile);
if(file !=null){
TotalFound++;
buffer.post(file);
lblProgress.Text = String.Format("{0}", TotalFound);
}
}, options);
上面的代码冻结了我的Form,即使我使用“TaskScheduler.FromCurrentSynchronizationContext”,为什么?因为当我使用下面的代码时,我的表单更新
DataflowBlockOptions options = new DataflowBlockOptions(){ TaskScheduler = uiScheduler, };
var producer = new ActionBlock<someObject>(largeFile=>{
var file = GetFileFromLargeFile(largeFile);
if(file !=null){
Task.Factory.StartNew(() => {
TotalFound++;
buffer.Post(file);
}).ContinueWith(uiTask => {
lblProgress.Text = String.Format("{0}", TotalFound);
},CancellationToken.None, TaskContinuationOptions.None, uiScheduler);
}
});
我是整个TPL数据流的新手,所以我希望有人可以分享一下为什么在第二个代码片段中它起作用的原因,而在第一个代码片段中却没有。
亲切的问候, Martijn
答案 0 :(得分:5)
您的用户界面被阻止的原因是因为您正在使用FromCurrentSynchronizationContext
。它会导致代码在UI线程上运行,这意味着如果您正在执行一些长时间运行的操作(很可能是GetFileFromLargeFile()
),它将会冻结。
另一方面,您必须在UI线程上运行lblProgress.Text
。
我不确定你应该在这段代码中直接设置lblProgress.Text
,它看起来与我的联系过于紧密。但是如果你想这样做,我认为你应该在UI线程上运行该行:
var producer = new ActionBlock<someObject>(async largeFile =>
{
var file = GetFileFromLargeFile(largeFile);
if (file != null)
{
TotalFound++;
await buffer.SendAsync(file);
await Task.Factory.StartNew(
() => lblProgress.Text = String.Format("{0}", TotalFound),
CancellationToken.None, TaskCreationOptions.None, uiScheduler);
}
});
但更好的解决方案是,如果你使GetFileFromLargeFile()
异步,并确保它不在UI线程上执行任何长时间运行的操作(ConfigureAwait(false)
可以帮助你)。如果你这样做了,ActionBlock
的代码可以在UI线程上运行,而不会冻结你的UI:
var producer = new ActionBlock<someObject>(async largeFile =>
{
var file = await GetFileFromLargeFile(largeFile);
if (file != null)
{
TotalFound++;
await buffer.SendAsync(file);
lblProgress.Text = String.Format("{0}", TotalFound)
}
}, options);