2012-06-24 65 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 - 更新了答案... –