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 は、スケジューラでタスクを実行します(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 に答える