2012-06-24 66 views
2

我想通過隊列迭代 - 從隊列中取1項,在後臺任務中處理它,更新UI,然後取下一個項目,依此類推。問題是第一個項目在後臺任務(線程)中處理,但隨後的項目在UI線程中處理 - 阻止UI。遞歸任務隊列

有誰知道爲什麼發生這種情況,以及如何解決這個問題?我的完整測試代碼如下。注意:這段代碼是爲了我的學習和將來的參考 - 不是任何真實的應用程序。

public partial class MainWindow : Window 
{ 
    private Queue<int> testQueue = new Queue<int>(); 
    private TaskScheduler uiScheduler; 

    public MainWindow() 
    { 
     InitializeComponent(); 

     this.uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 
     this.testQueue = new Queue<int>(); 
     this.testQueue.Enqueue(3); 
     this.testQueue.Enqueue(6); 
     this.testQueue.Enqueue(7); 
     this.testQueue.Enqueue(11); 
     this.testQueue.Enqueue(13); 
    } 

    // just a method that takes about 1 second to run on a modern pc 
    private double SumRootN(int root) 
    { 
     double result = 0; 
     for (int i = 1; i < 10000000; i++) 
     { 
      result += Math.Exp(Math.Log(i)/root); 
     } 
     return result; 
    } 

    private void testQueueButton_Click(object sender, RoutedEventArgs e) 
    { 
     this.processQueue(); 
    } 

    private void processQueue() 
    { 
     if (this.testQueue.Count > 0) 
     { 
      int root = this.testQueue.Dequeue(); 
      Task<double>.Factory.StartNew(() => SumRootN(root)) 
       .ContinueWith(t => 
       { 
        this.statusText.Text += String.Format("root {0} : {1}\n", root, t.Result); 
        this.processQueue(); 
       }, uiScheduler); 
     } 
     else 
     { 
      this.statusText.Text += "Done\n"; 
     } 
    } 
} 

回答

3

感謝您發佈一個repro,它允許我進行調試。

Task.Factory.StartNew在調度程序(factoryScheduler ?? currentTaskScheduler ?? threadPoolScheduler)上運行您的任務。你進入了第二種情況:你的新任務從它的父節點繼承調度器。

我注意到你好奇的使用遞歸調用來模擬一個循環。如果你不喜歡這樣,問題消失:

  Task<double>.Factory.StartNew(() => SumRootN(root)) 
      .ContinueWith(t => 
      { 
       this.statusText.Text += String.Format("root {0} : {1}\n", root, t.Result); 
      }, uiScheduler).ContinueWith(t => { this.processQueue(); }); 
+0

非常感謝!我錯誤地假設了。 – flolim

+0

我也是。我調試了這個,因爲我無法相信它。 – usr

1

這是因爲您正在使用TaskScheduler.FromCurrentSynchronizationContext() - 您確實知道它的作用是對的嗎? (使得它在同一個線程的叫法,在你的情況下,UI上運行)

編輯: USR回答你這是爲什麼發生的事情,但你也可以做到這一點(把準並行處理):

int root = this.testQueue.Dequeue(); 
    Task<double>.Factory.StartNew(() => SumRootN(root)) 
     .ContinueWith(t => 
     { 
      this.statusText.Text += String.Format("root {0} : {1}\n", root, t.Result); 
     }, uiScheduler); 
    this.processQueue(); 
+0

是的,後續任務並在UI線程,但第一個任務'任務 .Factory.StartNew上運行(()=> SumRootN(根)) '應該在​​不同的線程上運行,因爲我沒有爲它指定一個任務調度器? – flolim

+0

@ flolim - 更新了答案... –