2012-05-20 123 views
2

我目前正在从一个大的二进制文件中读取应用程序,该文件包含数千个文件,每个文件正由应用程序中的其他类处理。该类返回一个对象或null。我想在主表单上显示进度,但出于某种原因,我无法理解它。FromCurrentSynchronizationContext,我错过了什么?

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

上面的代码冻结我的表,甚至我用“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数据流,所以我希望有人能分享为什么在第二代码片段它的工作原理,并在第一个片段的一些光它没有。

亲切的问候, 马亭

回答

4

你的UI被阻塞是becase的你使用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); 
+0

TNX的解释,明确给我认识。仍然有一个问题,像在第一个例子中那样在UI线程外部运行任务会更好吗?我们这样做对于应用程序的性能无关紧要吗? – Martijn

+0

如果任务只运行很短的时间,那么它并不重要。否则,你不应该在UI线程上运行长时间运行的任务。不是因为性能,而是因为它会冻结你的应用程序。 – svick

+0

是否有任何其他(更好)的方式来处理gui更新?我使用svick推荐的代码,但仍然是我的UI冻结..在我的应用程序中,总共有8个ActionBlock 每个执行一个自定义对象的任务,3个ActionBlocks执行有一个MaxParallizism设置为处理器计数,在我的情况是7.所有8个ActionBlocks与gui进行通信......因此,理论上它可能是> 30个想要更新gui的任务。任何想法? – Martijn