2012-05-20 174 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