递归任务队列

时间:2012-06-24 22:15:41

标签: c# .net .net-4.0 task-parallel-library

我正在尝试遍历队列 - 从队列中取出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";
        }
    }
}

2 个答案:

答案 0 :(得分:3)

感谢您发布一个允许我调试的repro。

Task.Factory.StartNew在调度程序(factoryScheduler ?? currentTaskScheduler ?? threadPoolScheduler)上运行您的任务。您进入案例2:您的新任务从其父级继承调度程序。

我注意到你好奇地使用递归调用来模拟一个循环。如果你这样做,问题就会消失:

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

答案 1 :(得分:1)

那是因为您正在使用TaskScheduler.FromCurrentSynchronizationContext() - 您确实知道它的作用是什么吗? (使其在调用的同一个线程上运行,在您的情况下是UI)

编辑:我们回答你为什么会这样,但你也可以这样做(对于准并行处理):

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