3

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

2 回答 2

3

感谢您发布允许我调试的重现。

Task.Factory.StartNew 在 scheduler 上运行您的任务(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(); });
于 2012-06-24T22:37:09.477 回答
0

那是因为您正在使用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();
于 2012-06-24T22:22:49.533 回答